Search code examples
javaandroidandroid-studiojavafxjavafxports

How to run JavaFX in Android Studio and make ImageView


I have an interface with javafx.scene.image.Image class. I must implement this and create ImageView. So, how to add JavaFx into my Android Studio Project and how to do ImageView or other Image element in my activity?

User.java

import javafx.scene.image.Image;

    public interface User
    {
        String getName();
        int getId();
        Image getIcon();
    }

Solution

  • Based on JavaFXPorts and the Kokos sample, you can add JavaFX to an Android project created with Android Studio, and you will be able to run a JavaFX scene inside an Android fragment.

    Note that JavaFXPorts is initially intended to run full JavaFX projects on Android and iOS, using plain Java, without native code. Doing it the other way around is possible, but it is not the best approach.

    Anyway, these are the required steps to create a JavaFX scene with a JavaFX ImageView to hold a JavaFX Image.

    1. Create a simple project on Android Studio

    Select a Basic Activity template, and select the use of a fragment.

    Add a fragment

    2. Modify the build gradle files

    build.gradle (top level project)

    buildscript {
        repositories {
            jcenter()
        }
        dependencies {
            classpath 'me.tatarka:gradle-retrolambda:3.5.0'
            classpath 'com.android.tools.build:gradle:2.2.3'
        }
    }
    
    allprojects {
        repositories {
            jcenter()
        }
    }
    
    task clean(type: Delete) {
        delete rootProject.buildDir
    }
    

    build.gradle (module: app)

    In this file we'll include dalvik-sdk, the JavaFXPorts dependency for Android. If you use the Gluon plugin for your Java IDE to create mobile projects with the jfxmobile plugin, you can find it installed in:

    ~/.gradle/caches/modules-2/files-2.1/org.javafxports/dalvik-sdk/8.60.10/e...a/unpacked/dalvik-sdk
    

    Else, it can be downloaded from here. Extract it to a given path, let's say $dalvikPath.

    apply plugin: 'com.android.application'
    
    android {
        compileSdkVersion 26
        buildToolsVersion "26.0.3"
        defaultConfig {
            applicationId "gluonhq.com.myfragment"
            minSdkVersion 25
            targetSdkVersion 26
            versionCode 1
            versionName "1.0"
            multiDexEnabled true
            testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        }
        dexOptions {
            preDexLibraries = false
            additionalParameters=['--core-library']
        }
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
        }
        sourceSets {
            main {
                jniLibs.srcDir file("$dalvikPath/rt/lib")
                assets.srcDirs = ['assets']
            }
        }
        lintOptions {
            abortOnError false
        }
    }
    
    dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
        compile fileTree(include: ['*.jar'], dir: '$dalvikPath/rt/lib/ext')
        androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
            exclude group: 'com.android.support', module: 'support-annotations'
        })
        compile 'com.android.support:multidex:1.0.1'
        compile 'com.android.support:appcompat-v7:26.0.0-alpha1'
        compile 'com.android.support:design:26.0.0-alpha1'
        testCompile 'junit:junit:4.12'
    }
    

    3. Create a class that extends FXFragment

    In MainActivity.java:

    public static class PlaceholderFragment extends FXFragment {
    
        public PlaceholderFragment() {
            String fxapp = "your.package.myfragment.MainFX";
            this.setName(fxapp);
        }
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
        }
    }
    

    and call it from onCreate:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if (savedInstanceState == null) {
            getFragmentManager().beginTransaction()
                    .add(R.id.container, new PlaceholderFragment())
                    .commit();
        }
        ...
    }
    

    Note that android:id="@+id/container" is required in activity_main.xml.

    4. Final step: create the JavaFX class

    Make sure the package name is your.package.myfragment.

    MainFX.java

    public class MainFX extends Application {
    
        @Override
        public void start(Stage stage) throws Exception {
    
            final ImageView imageView = new ImageView();
            imageView.setImage(new Image("https://upload.wikimedia.org/wikipedia/en/c/cc/JavaFX_Logo.png"));
            imageView.setPreserveRatio(true);
    
            Screen primaryScreen = Screen.getPrimary();
            Rectangle2D visualBounds = primaryScreen.getVisualBounds();
            double width = visualBounds.getWidth();
            double height = visualBounds.getHeight();
    
            StackPane stackPane = new StackPane(imageView);
            Scene scene = new Scene(stackPane, width, height);
    
            imageView.fitWidthProperty().bind(scene.widthProperty().subtract(20));
            stage.setScene(scene);
            stage.show();
        }
    
    }
    

    Note that this is a pure JavaFX Application class, and that you need to set the stage size base on the screen bounds.

    Deploy and run the app

    Note that you will have JavaFX running in an Android view, but you still have other Android components on different views on top of it, depending on your layout.