android - 如何在 Android 上运行 JavaFX

标签 android gradle javafx javafxports

我尝试在 Android 上运行一个简单的 JavaFX 应用程序。因此我读了javafxports Getting Started Guide使用 gradle 构建应用程序,但我被困在某个地方。我可以在 Eclipse 中使用 gradle 的 installDebug 任务在 Android 设备上构建和安装该应用程序,但是,当我启动该应用程序时,出现黑屏。当我提取 .apk 文件时,它不包含 JavaFX 应用程序的 jar 文件。我假设 apk 应该包含 JavaFX 应用程序 jar 文件,但我不知道如何将它包含到 .apk 中。

我还尝试使用 gradle 构建 JavaFX 应用程序本身(jar 文件),效果很好。但是,我不知道把这个 jar 文件放在哪里,它可以包含在 apk 文件中。我读到我必须将它放入 dist 目录,但我认为这只会在 Netbeans 中使用,对吗?我正在使用 Eclipse gradle 集成来构建项目。

这是我尝试过的。由于 JavaFX 应用程序与 HelloWorld 示例应用程序一样简单,并且运行起来非常棒,我希望我的 gradle 构建文件中存在配置错误。

build.gradle - 构建 apk

apply plugin: 'me.tatarka.retrolambda'
apply plugin: 'android'

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'me.tatarka:gradle-retrolambda:2.5.0'
        classpath 'com.android.tools.build:gradle:1.0.1'
    }
}
repositories {
    jcenter()
}
ext {
    dalvikSdkHome = getProjectProperty('dalvikSDK')
    dalvikSdkLib = dalvikSdkHome + '/rt/lib'
}

android {
    compileSdkVersion 21
    buildToolsVersion "21.1.1"
    dexOptions {
        preDexLibraries = false
    }
    sourceSets {
        main {
            jniLibs.srcDir file("${dalvikSdkLib}/")
            assets.srcDirs = ['assets']
        }
    }
    lintOptions {
        abortOnError false
    }
}

dependencies {
    compile files ("${dalvikSdkLib}/ext/jfxrt.jar",
                   "${dalvikSdkLib}/ext/jfxdvk.jar",
                   "${dalvikSdkLib}/ext/compat-1.0.0.jar")
}

project.tasks.withType(com.android.build.gradle.tasks.Dex) {
    additionalParameters=['--core-library']
}

String getProjectProperty(String propertyName) {
    project.hasProperty(propertyName) ? project.property(propertyName) : null
}

build.gradle - 构建 jar

// Declares binary plugin and its required JavaFX classpath
apply from: "http://dl.bintray.com/content/shemnon/javafx-gradle/0.4.0/javafx.plugin"

// Configures plugin
javafx {
    // Points to JDK and its JavaFX libraries, also declares target runtime JDK
    javaRuntime = getProjectProperty('javaJDKPath')

    // Application name and ID presented by target OS
    appID 'HelloWorldApp'
    appName 'Hello World Application'

    // Main class of application
    mainClass 'helloworld.HelloWorld'

    // JVM arguments, system properties, application command line arguments
    jvmArgs = ['-XX:+AggressiveOpts', '-XX:CompileThreshold=1']
    systemProperties = ['prism.disableRegionCaching':'true']
    arguments = ['-l', '--fast']

    // Keystore credentials for signing JAR
    // Generate key: keytool -genkey -alias release -keyalg RSA -keystore keystore.jks -keysize 2048
    releaseKey {
        alias = 'release'
        keyStore = file(getProjectProperty('keystoreJKSFile')) // keyStore = file("${System.properties['user.home']}/keystore/keystore.jks")
        keyPass = getProjectProperty('keyStorePassword')
        storePass = getProjectProperty('storePassword')
    }

    signingMode 'release'
    // ...
}

String getProjectProperty(String propertyName) {
    project.hasProperty(propertyName) ? project.property(propertyName) : null
}

gradle.properties

javaJDKPath=D:/Java/jdk1.8.0_20
dalvikSDK=D:/Java/dalvik-sdk-8u20b3/dalvik-sdk
keystoreJKSFile=D:/Java/jre1.8.0_20/bin/keystore.jks
keyStorePassword=password
storePassword=password

local.properties

sdk.dir=D:/programme/Android/adt-bundle-windows-x86_64-20130917/sdk

这是我的项目结构

HelloWorld
-- src\main
    -- java\helloworld\HelloWorld.java
    -- res\
    -- AndroidManifest.xml
-- assets\
    -- javafx.platform.properties
    -- javafx.properties
-- build.gradle
-- gradle.properties
-- local.properties

我需要使用 dist 目录吗?我应该将我的 JavaFX 应用程序的 jar 文件放在哪里,它将包含在 apk 文件中?

最佳答案

现在回答我自己的问题。 如我的问题中所述,gradle 设置的配置已经在运行。在我的问题中,我没有向您展示我正在使用 .fxml 文件进行布局。所以我没有让它运行的原因是因为 .fxml 布局没有放在正确的位置以与 apk 文件一起打包(LogCat 显示错误 Location Not Found 并且我有一个我的设备出现黑屏)。

首先,这是 HelloWorld.java 的工作示例(请参阅我的问题中的结构和 gradle 设置等):

package helloworld;

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class HelloWorld extends Application {
    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {
        primaryStage.setTitle("Hello World!");
        Button btn = new Button();
        btn.setText("Say 'Hello World'");
        btn.setOnAction(new EventHandler<ActionEvent>() {

            @Override
            public void handle(ActionEvent event) {
                System.out.println("Hello World!");
            }
        });

        StackPane root = new StackPane();
        root.getChildren().add(btn);
        primaryStage.setScene(new Scene(root, 300, 250));
        primaryStage.show();
    }
}

使用 fxml 布局在 Android 上运行 JavaFX

如果您想使用.fxml 文件,您需要稍微更改项目的结构。所有.fxml 文件、.css 文件、图形等都属于resources\assets 目录或子目录。这样可以确保 .fxml 文件等将被打包到 apk 中。

HelloWorld
-- src\main
    -- java\helloworld\
        -- HelloWorld.java
        -- MyController.java
    -- resources\assets\
        -- sample_layout.fxml
    -- AndroidManifest.xml
-- assets\
    -- javafx.platform.properties
    -- javafx.properties
-- build.gradle
-- gradle.properties
-- local.properties

我没有检查是否包含 javafx.platform.propertiesjavafx.propertiesassets 文件夹(均来自dalvikVM sdk) 仍然是必需的。如果我检查 .apk 文件的内容,apk 包含这两个文件两次。似乎 dalvikVM 库会自动复制这些文件。

注意:如果您需要检查 apk 的内容,请提取 apk 文件,然后提取作为 apk 一部分的 classes.dex (see this post for more details)

这是一个使用 .fxml 文件的示例:

HelloWorld.java

package helloworld;

import java.io.IOException;
import java.net.URL;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;

public class HelloWorld extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {
        try {
            URL fxmlFile = getClass().getResource("/assets/sample_layout.fxml");
            FXMLLoader loader = new FXMLLoader(fxmlFile);
            AnchorPane page = (AnchorPane) loader.load();
            MyController controller = (MyController) loader.getController();
            Scene scene = new Scene(page);
            primaryStage.setScene(scene);
            primaryStage.setTitle("JavaFX Sample");
            primaryStage.show();
        } catch(IOException e) {
            e.printStackTrace();
        }
    }

}

MyController.java

package helloworld;

import java.net.URL;
import java.util.ResourceBundle;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;

public class MyController {

    @FXML
    private ResourceBundle resources;
    @FXML
    private URL location;
    @FXML
    private Label label_Counter;
    @FXML
    private Button button_IncrementCounter;
    @FXML
    private Button button_DecrementCounter;

    private static final String OUTPUT_PREFIX = "Counter: ";
    private static int counter = 0;

    @FXML
    void onIncrementButtonPressed(ActionEvent event) {
        label_Counter.setText(OUTPUT_PREFIX + ++counter);
    }

    @FXML
    void onDecrementButtonPressed(ActionEvent event) {
        label_Counter.setText(OUTPUT_PREFIX + --counter);
    }

    @FXML
    void initialize() {
        assert label_Counter != null : "fx:id=\"label_Counter\" was not injected: check your FXML file 'sample_layout.fxml'.";
        assert button_IncrementCounter != null : "fx:id=\"button_IncrementCounter\" was not injected: check your FXML file 'sample_layout.fxml'.";
        assert button_DecrementCounter != null : "fx:id=\"button_DecrementCounter\" was not injected: check your FXML file 'sample_layout.fxml'.";
        label_Counter.setText(OUTPUT_PREFIX + 0);
    }

}

sample_layout.fxml

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

<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>

<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="helloworld.MyController">
   <children>
      <VBox layoutX="332.0" layoutY="71.0" prefHeight="400.0" prefWidth="600.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
         <children>
            <Label text="Please click on the buttons to increment or decrement the counter:" />
            <Button fx:id="button_IncrementCounter" mnemonicParsing="false" onAction="#onIncrementButtonPressed" text="Increment Counter">
               <VBox.margin>
                  <Insets top="10.0" />
               </VBox.margin>
               <padding>
                  <Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
               </padding>
            </Button>
            <Button fx:id="button_DecrementCounter" mnemonicParsing="false" onAction="#onDecrementButtonPressed" text="Decrement Counter">
               <VBox.margin>
                  <Insets top="10.0" />
               </VBox.margin>
               <padding>
                  <Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
               </padding>
            </Button>
            <Label fx:id="label_Counter" text="&lt;output-placeholder&gt;">
               <VBox.margin>
                  <Insets top="20.0" />
               </VBox.margin>
            </Label>
         </children>
         <padding>
            <Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
         </padding>
      </VBox>
   </children>
</AnchorPane>

希望能帮助其他人在 Android 上开始使用 JavaFX。

关于android - 如何在 Android 上运行 JavaFX,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27794717/

相关文章:

gradle - 如何在 gradle 中使用 JMH?

java - 如何将 build.gradle 文件拆分为具有较小任务的文件

java - ComboBox 选项在调整舞台大小时获得 margin

java - Android Studio : Plugin with id 'com.android.application' not found

android - 隐藏 Android 键盘按键预览

java - 在警报对话框中获取用户选择

android - 在 ExoPlayer 中检测暂停/恢复

java - “java dynamic web project”和 “Gradle project”有什么区别

Javafx- 鼠标单击事件可撤消上一次鼠标单击所做的操作

java - 如何使用 fxml 元素上的放置事件? (javafx)