2

I know that in order to get a single image with name already known I can just use

getClass().getResource()..

However, what if I have many images in a specific folder? I don't want to have to take every single Image name and call the getResource() method.

The following works on Desktop, but causes a crash on Android:

public void initializeImages() {

        String platform = "android";

        if(Platform.isIOS())
        {
            platform = "ios";
        } else if(Platform.isDesktop())
        {
            platform = "main";
        }

        String path = "src/" + platform + "/resources/com/mobileapp/images/";


        File file = new File(path);
        File[] allFiles = file.listFiles();

        for (int i = 0; i < allFiles.length; i++) {
            Image img = null;
            try {
                img = ImageIO.read(allFiles[i]);
                files.add(createImage(img));
            } catch (IOException ex) {
                Logger.getLogger(ImageGroupRetriever.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

    }

 //Taken from a separate SO question. Not causing any issues
 public static javafx.scene.image.Image createImage(java.awt.Image image) throws IOException {
        if (!(image instanceof RenderedImage)) {
            BufferedImage bufferedImage = new BufferedImage(image.getWidth(null),
                    image.getHeight(null), BufferedImage.TYPE_INT_ARGB);
            Graphics g = bufferedImage.createGraphics();
            g.drawImage(image, 0, 0, null);
            g.dispose();

            image = bufferedImage;
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ImageIO.write((RenderedImage) image, "png", out);
        out.flush();
        ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
        return new javafx.scene.image.Image(in);
    }

Is there something else I need to consider in the directory structure?

A.Sharma
  • 2,771
  • 1
  • 11
  • 24

1 Answers1

3

If you run that code on Android, you will see the exception using adb logcat -v threadtime:

Caused by: java.lang.NullPointerException: Attempt to get length of null array

at the line where you call allFiles.length inside the for loop.

On Android you can't read the path in the same way as you do on Desktop. And it doesn't make a difference if you copy the images to src/android/resources.

If you check the build/javafxports/android/ folder you'll find the apk, and if you expand it on your IDE you'll see that the images are just placed under com.mobileapp.images.

That's why the usual getClass().getResource("/com/mobileapp/images/<image.png>") works.

What you can do is add a zip file with all the images to a known location. Then use the Charm Down Storage plugin to copy the zip to the app's private folder on Android, extract the images, and finally you will be able to use File.listFiles over the private path on the device.

This works for me, providing you have a zip named images.zip under com/mobileapp/images with all the files:

private List<Image> loadImages() {
    List<Image> list = new ArrayList<>();

    // 1 move zip to storage
    File dir;
    try {
        dir = Services.get(StorageService.class)
                .map(s -> s.getPrivateStorage().get())
                .orElseThrow(() -> new IOException("Error: PrivateStorage not available"));

        copyZip("/com/mobileapp/images/", dir.getAbsolutePath(), "images.zip");
    } catch (IOException ex) {
        System.out.println("IO error " + ex.getMessage());
        return list;
    }

    // 2 unzip
    try {
        unzip(new File(dir, "images.zip"), new File(dir, "images"));
    } catch (IOException ex) {
        System.out.println("IO error " + ex.getMessage());
    }

    // 3. load images
    File images = new File(dir, "images");
    for (int i = 0; i < images.listFiles().length; i++) {
        try {
            list.add(new Image(new FileInputStream(images.listFiles()[i])));
        } catch (FileNotFoundException ex) {
            System.out.println("Error " + ex.getMessage());
        }

    }
    return list;
}

public static void copyZip(String pathIni, String pathEnd, String name)  {
    try (InputStream myInput = BasicView.class.getResourceAsStream(pathIni + name)) {
        String outFileName =  pathEnd + "/" + name;
        try (OutputStream myOutput = new FileOutputStream(outFileName)) {
            byte[] buffer = new byte[1024];
            int length;
            while ((length = myInput.read(buffer)) > 0) {
                myOutput.write(buffer, 0, length);
            }
            myOutput.flush();

        } catch (IOException ex) {
            System.out.println("Error " + ex);
        }
    } catch (IOException ex) {
        System.out.println("Error " + ex);
    }
}

public static void unzip(File zipFile, File targetDirectory) throws IOException {
    try (ZipInputStream zis = new ZipInputStream(
            new BufferedInputStream(new FileInputStream(zipFile)))) {
        ZipEntry ze;
        int count;
        byte[] buffer = new byte[8192];
        while ((ze = zis.getNextEntry()) != null) {
            File file = new File(targetDirectory, ze.getName());
            File dir = ze.isDirectory() ? file : file.getParentFile();
            if (!dir.isDirectory() && !dir.mkdirs())
                throw new FileNotFoundException("Failed to ensure directory: " + dir.getAbsolutePath());
            if (ze.isDirectory())
                continue;
            try (FileOutputStream fout = new FileOutputStream(file)) {
                while ((count = zis.read(buffer)) != -1)
                    fout.write(buffer, 0, count);
            }
        }
    }
}

Note this will work as well on Desktop and iOS.

The unzip method is based on this answer.

Community
  • 1
  • 1
José Pereda
  • 44,311
  • 7
  • 104
  • 132
  • Thanks Jose! I'm sure I'll have more gluon questions as I'm just getting started with it. I can see that you're the SO guru for it. I appreciate the help. – A.Sharma Jan 10 '17 at 21:40