Recognizing Dragged File Types in JavaFX

Dragging files onto nodes in JavaFX is fairly easy. The main trick is making sure you are only accepting the correct file types. There are several approaches to determining file type on the fly, or on the drag over, in our case. However, I recommend just going with the simplest approach.

While you are dealing with the drag over event, get the Dragboard from the event. Then grab the List<File> from the JavaFX DragBoard. At that point it is simple to get the first file’s Path, convert it to a String, and check that it ends with an acceptable file extension.

No. Really. It’s simple.

Here’s the code for checking for a *.txt file being dragged on to a TextArea that is watching drag over events.

@FXML
void textDragOver(DragEvent event) {
  Dragboard board = event.getDragboard();
  if (board.hasFiles()) {

    List<File> phil = board.getFiles();
    String path = phil
        .get(0)
        .toPath()
        .toString();

    if (path.endsWith(".txt")) {
      event
      .acceptTransferModes(
          TransferMode.ANY);
    }        
  }
}

 

Note: If this is your first introduction to drag and drop in JavaFX, then you should look at my text-file dropping tutorial. Also, I have a JavaFX image-file dropping tutorial that you should look at.

The one catch to handling file type checking while watching drag over events, is that drag over events are repeatedly triggered while the mouse is moving over the receiving area. Don’t try to do a highly disk intensive, or CPU intensive check while doing a drag over, because you will be repeating it 10’s or even 100’s of times per second.

I created a quick tutorial for dropping image files and text files. It distinguishes between the two types of files, and only lets the proper type drop in the TextArea and the Image view.

JavaFX TextArea and ImageView
This JavaFX example project shows drag-and-drop allowing only the proper types of files to be dropped in the proper locations.

You need three files to reproduce this example project: DropChoice.fxml, Main.java, and DropControl.java.

Here’s the Main.java.

package com.whatisjavafx;
  
import java.net.URL;

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


public class Main extends Application {
  @Override
  public void start(Stage stage) {
    try {
      URL url = 
          getClass()
          .getResource("DropChoice.fxml");
      HBox root = FXMLLoader.load(url);

      Scene scene = new Scene(root);
      stage.setScene(scene);
      stage
      .setTitle("Drop Control");
      stage.show();
    } catch(Exception e) {
      e.printStackTrace();
    }
  }
  
  public static void main(String[] args) {
    launch(args);
  }
}

 

Here’s the DropControl.java

package com.whatisjavafx;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;

import javafx.fxml.FXML;
import javafx.scene.control.TextArea;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.DragEvent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.TransferMode;

public class DropControl {

  @FXML
  private ImageView myImageView;

  @FXML
  private TextArea myTextArea;

  @FXML
  void imageDragDropped(DragEvent event) {
    try {
      Dragboard board = event.getDragboard();
      List<File> phil = board.getFiles();
      FileInputStream fis;
        fis = new FileInputStream(phil.get(0));
      Image image = new Image(fis);
      myImageView.setImage(image);
    } catch (FileNotFoundException e) {
      e.printStackTrace();
    }
  }

  @FXML
  void imageDragOver(DragEvent event) {
    Dragboard board = event.getDragboard();
    if (board.hasFiles()) {

      List<File> phil = board.getFiles();
      String path = phil
          .get(0)
          .toPath()
          .toString();

      if ( 
          path.endsWith(".jpeg") || 
          path.endsWith(".jpg") || 
          path.endsWith(".png") || 
          path.endsWith(".gif")
          ) {
        event
        .acceptTransferModes(
            TransferMode.ANY);
      }        
    }
  }

  @FXML
  void textDragDropped(DragEvent event) {
    try {
      Dragboard board = event.getDragboard();
      List<File> phil = board.getFiles();
      FileInputStream fis;
      fis = new FileInputStream(phil.get(0));

      StringBuilder builder = new StringBuilder();
      int ch;
      while((ch = fis.read()) != -1){
        builder.append((char)ch);
      }

      fis.close();

      myTextArea.setText(builder.toString());

    } catch (FileNotFoundException fnfe) {
      fnfe.printStackTrace();
    } catch (IOException ioe) {
      ioe.printStackTrace();
    }
  }

  @FXML
  void textDragOver(DragEvent event) {
    Dragboard board = event.getDragboard();
    if (board.hasFiles()) {
  
      List<File> phil = board.getFiles();
      String path = phil
          .get(0)
          .toPath()
          .toString();
  
      if (path.endsWith(".txt")) {
        event
        .acceptTransferModes(
            TransferMode.ANY);
      }        
    }
  }

  @FXML
  void initialize() {}
}

 

Here’s the DropChoice.fxml. If you’d rather see it in a RAD tool, then save this FXML to disk, and open it with Scene Builder.

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

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>


<HBox 
maxHeight="-Infinity" 
maxWidth="-Infinity" 
minHeight="-Infinity"
minWidth="-Infinity" 
prefHeight="400.0" 
prefWidth="600.0"
xmlns="http://javafx.com/javafx/8.0.65" 
xmlns:fx="http://javafx.com/fxml/1"
fx:controller="com.whatisjavafx.DropControl">
 <children>
  <VBox 
  prefHeight="200.0" 
  prefWidth="100.0">
   <children>
    <Label text="Image View">
     <VBox.margin>
      <Insets left="10.0" />
     </VBox.margin>
    </Label>
    <ImageView 
    fx:id="myImageView" 
    fitHeight="360.0" 
    fitWidth="280.0"
    onDragDropped="#imageDragDropped" 
    onDragOver="#imageDragOver"
    pickOnBounds="true" 
    preserveRatio="true">
     <VBox.margin>
      <Insets 
      bottom="10.0" 
      left="10.0" 
      right="10.0" 
      top="10.0" />
     </VBox.margin>
     <image>
      <Image url="@background.gif" />
     </image>
    </ImageView>
   </children>
  </VBox>
  <VBox 
  prefHeight="200.0" 
  prefWidth="100.0">
   <children>
    <Label text="Text Area">
     <VBox.margin>
      <Insets left="10.0" />
     </VBox.margin>
    </Label>
    <TextArea 
    fx:id="myTextArea" 
    minHeight="360.0" 
    minWidth="280.0"
    onDragDropped="#textDragDropped" 
    onDragOver="#textDragOver"
    prefHeight="360.0" 
    prefWidth="280.0">
     <VBox.margin>
      <Insets 
      bottom="10.0" 
      left="10.0" 
      right="10.0" 
      top="10.0" />
     </VBox.margin>
    </TextArea>
   </children>
  </VBox>
 </children>
</HBox>

 

The FXML in this example consists of a HBox that holds two VBoxes. The VBoxes each have a Label and a TextArea or an ImageView. I added methods for listening to drag over events and also methods that handle the actual drop.

This FXML could be replaced with a pure Java JavaFX layout. The real action happens in the Controller, where the file types are checked and then the image or text file is loaded and placed in the view.

That’s really all there is to a basic drop-file functionality in JavaFX.

 

Leave a Reply

Your email address will not be published. Required fields are marked *