Как запустить JavaFX на Android

Я пытаюсь запустить простое приложение JavaFX на Android. Поэтому я прочитал Руководство по началу работы с javafxports, используя gradle для создания приложения, но где-то застрял. Я могу создать и установить приложение на устройстве Android, используя задачу installDebug gradle в Eclipse, однако, когда я запускаю приложение, я получаю черный экран. Когда я извлекаю файл .apk, он не содержит jar-файла приложения JavaFX. Я предполагаю, что apk должен содержать jar-файл приложения JavaFX, но я понятия не имею, как включить его в файл .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? Куда мне поместить jar-файл моего приложения JavaFX, чтобы он был включен в apk-файл?


person Michael    schedule 06.01.2015    source источник
comment
Возможно, эта ссылка поможет вам начать работу: javafxports.org/page/home, а также эта статья. : infoq.com/articles/Building-JavaFX-Android-Apps   -  person edharned    schedule 06.01.2015
comment
Эй, Эдхарнед, спасибо за ответ. Я уже посетил обе стороны раньше. Насколько я помню, второй сайт использует старый подход, создавая приложение с помощью ant (+gradle). Руководство по началу работы с javafxports показывает, что можно создать приложение/apk только с помощью gradle. Это то, что я хочу (извините, что не указал на это). Я уже исправил проблему вчера и опубликую исходный код в эту пятницу.   -  person Michael    schedule 08.01.2015
comment
Хорошо, я начинаю заменять Swing на JavaFX. Следующий проект будет портирован на Android-проекты, так что с нетерпением жду вашего обновления.   -  person edharned    schedule 08.01.2015
comment
@edharned Я разместил свой источник, см. ответ ниже. Надеюсь, это поможет вам и всем остальным начать работу с JavaFX на Android.   -  person Michael    schedule 22.01.2015
comment
Спасибо. Никто никогда не говорил, что будет легко   -  person edharned    schedule 22.01.2015
comment
Примечание. Я отредактировал конфигурацию Gradle (build.gradle), чтобы использовать новейшую версию плагина Android и плагина me.tatarka.retrolambda, так как он был обновлен несколько дней назад. Я получил ошибки зависимостей при обновлении зависимостей градиента. С этим обновлением это исправлено. Ошибка была: https://jcenter.bintray.com/com/android/tools/build/gradle/maven-metadata.xml Required by: MyProjectName:unspecified > me.tatarka:gradle-retrolambda:2.4.1   -  person Michael    schedule 26.01.2015


Ответы (2)


Теперь отвечаю на свой вопрос. Конфигурация установки gradle, как указано в моем вопросе, уже запущена. В моем вопросе я не показал вам, что использовал файл .fxml для макета. Итак, причина, по которой я не запустил его, заключалась в том, что макет .fxml не был размещен в нужном месте для упаковки с файлом apk (LogCat показал ошибку Location Not Found, и у меня был черный экран на моем устройстве).

Прежде всего, вот рабочий образец для HelloWorld.java (см. структуру и настройку градиента и т. д. в моем вопросе):

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

Запуск JavaFX на Android с использованием макетов fxml

Если вы хотите использовать файлы .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

Я не проверял, нужна ли еще папка assets, содержащая javafx.platform.properties и javafx.properties (оба из dalvikVM sdk). Если я проверю содержимое файла .apk, apk содержит оба файла дважды. Похоже, что библиотека dalvikVM автоматически копирует эти файлы.

Примечание. Если вам нужно проверить содержимое вашего apk, извлеките файл apk, а затем извлеките class.dex, который является частью apk (подробности см. в этом посте)

Вот пример использования файлов .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>

Надеюсь, это поможет всем остальным начать работу с JavaFX на Android.

person Michael    schedule 21.01.2015

Как запустить javafx на андроиде:

  1. Загрузите dalvik sdk.
  2. Перейти к samples\HelloWorld\javafx - это проект Gradle
  3. Изменить местоположение dalvik-sdk и android-sdk в local.properties
    Пример в моей системе Windows:
    sdk.dir=C\:\\dev\\android-sdk javafx.dir=C\:\\dev\\dalvik-sdk
  4. Запустите gradlew installDebug для сборки и установки apk на устройство. Вы также найдете результаты в папке build
  5. Запустите приложение на устройстве. (Я видел черный экран за 10 секунд до первого запуска)
  6. Откройте проект в Eclipse или Idea как стандартный проект Gradle
person zella    schedule 07.01.2015