「*[JavaFX]」の検索結果を表示しています
2014-03-11 08:24

DragboardChecker

Thanks to JavaFX Drag & Drop function, you can drag something from a third-party(native) application such as Finder(Mac OS).
You just need to implement of the DragOver and DragDropped event hander on a Target(see code1).
This is exciting for me, so I just want to know what kind of content the dragboard has.
This application checks dragboard contents and shows them to the screen.

f:id:tomoTaka:20140310080809p:image:w360

When you drag files from Finder(Max OS 10.9) to this App, the dragboard has files and 1 Dataformat.
f:id:tomoTaka:20140310074624p:image:w480

When you drag URL from web browser to this, the dragboard has string, URL, files and 3 of Dataformat.
f:id:tomoTaka:20140310074652p:image:w480

f:id:tomoTaka:20140311075106p:image:w360f:id:tomoTaka:20140311075127p:image:w360

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        initContextCheckers();
        this.dataFormatListView.setCellFactory(p -> new DataFormatCell()); // <― see code 4
        this.dropHereImage.setOnDragOver(e -> {
            e.acceptTransferModes(TransferMode.ANY);
            e.consume();
        });
        this.dropHereImage.setOnDragDropped(e -> {
            Dragboard db = e.getDragboard();
            this.contextCheckers.forEach(checker -> checker.check(db)); // <― see code2,3
            setDataFormatListView(db);
            e.setDropCompleted(true);
        });
    }
    private void initContextCheckers() {
        this.contextCheckers = Arrays.asList(
            new StringContextChecker(this.stringCheck, this.stringText)
          , new UrlContextChecker(this.urlCheck, this.urlText)
          , new FileContextChecker(this.filesCheck, this.filesText)
          , new RtfContextChecker(this.rtfCheck, this.rtfText)
          , new ImageContextChecker(this.imageCheck, this.imageView)
          , new HtmlContextChecker(this.htmlCheck, this.htmlText)
        );
    }    
    private void setDataFormatListView(Dragboard db) {
        List<DataFormatInfo> list = db.getContentTypes().stream().map(df -> {
            return new DataFormatInfo(df, db.getContent(df).toString());
        }).collect(Collectors.toList());       // <― see code5
        this.dataFormatListView.setItems(FXCollections.observableArrayList(list));
    }

This is the base class for each checker class like StringChecker, UrlChecker so on.

public abstract class ContextChecker<T extends Node> {
    protected BooleanProperty checkboxProp;
    protected T node;
    public ContextChecker(CheckBox checkbox, T node) {
        this.checkboxProp = new SimpleBooleanProperty(false);
        checkbox.selectedProperty().bind(this.checkboxProp);
        this.node = node;
    }
    abstract public void check(Dragboard db);    
}

Since clipboard(dragboard) has 6 methods for contains data, I create 6 checker class.

typemethodmethodchecker class
StringhasStringgetString StringContextChecker
FileshasFilesgetFilesFileContextChecker
HTMLhasHtmlgetHtmlHtmlContextChecker
ImagehasImagegetImageImageContextChecker
RTFhasRtfgetRtfRtfContextChecker
URLhasUrlgetUrlUrlContextChecker
public class StringContextChecker extends ContextChecker<TextArea>{
    public StringContextChecker(CheckBox checkbox, TextArea node) {
        super(checkbox, node);
    }
    @Override
    public void check(Dragboard db) {
        if (db.hasString()) {
            super.checkboxProp.set(true);
            super.node.setText(db.getString());
        } else {
            super.checkboxProp.set(false);
            super.node.clear();
        }
    }   
}

The clipboard(dragboard) has some custom data which are defined by data format.
To show the custom data, you can implement custom list cell like below.

public class DataFormatCell extends ListCell<DataFormatInfo>{
    GridPane root;
    Label dataFormatLabel;
    Text dataFormatText;
    Label contentLabel;
    Text contentText;
    Label idsLabel;
    ListView idsListView;
    ObservableList<String> idsList;
    
    public DataFormatCell() {
        init();
    }
    @Override
    protected void updateItem(DataFormatInfo info, boolean empty) {
        super.updateItem(info, empty);
        if (empty) {
            setText(null);
            setGraphic(null);
        } else {
            this.dataFormatText.setText(info.getDataFormat());
            this.contentText.setText(info.getContent());
            this.idsList.setAll(info.getIdentifiersList());
            setGraphic(root);
        }
    }
    private void init() {
        this.root = new GridPane();
        this.root.setPadding(new Insets(2));
        this.root.setHgap(2);
        this.root.setVgap(2);
        this.dataFormatLabel = new Label("Data Format:");
        this.dataFormatLabel.setPrefWidth(100);
        this.dataFormatText = new Text();
        this.dataFormatText.prefWidth(400);
        this.contentLabel = new Label("Content:");
        this.contentText = new Text();
        this.idsLabel = new Label("Identifires");        
        this.idsList = FXCollections.observableArrayList();
        this.idsListView = new ListView(this.idsList);
        this.idsListView.setPrefHeight(70);
        this.idsListView.setPrefWidth(500);        
        this.root.add(this.dataFormatLabel, 0, 0);
        this.root.add(this.dataFormatText, 1, 0);
        this.root.add(this.contentLabel, 0, 1);
        this.root.add(this.contentText, 1, 1);
        this.root.add(this.idsLabel, 0, 2);
        this.root.add(this.idsListView, 0, 3, 2, 1);
    }
}

This code has custom data information for DataFormatCell(see code4).

public class DataFormatInfo {
    private StringProperty dataFormatProp;
    private StringProperty contentProp;
    private List<String> identifiersList;    

    public DataFormatInfo(DataFormat dataFormat, String content) {
        this.dataFormatProp = new SimpleStringProperty(dataFormat.toString());
        this.contentProp = new SimpleStringProperty(content);
        this.identifiersList = 
            dataFormat.getIdentifiers().stream().collect(Collectors.toList());
    }
    public String getDataFormat(){
        return this.dataFormatProp.getValue();
    }
    public String getContent() {
        return this.contentProp.getValue();
    }
    public List<String> getIdentifiersList() {
        return this.identifiersList;
    }
}

The whole code is heretomoTaka01/DragboardChecker ? GitHub.
keep coding... ;-)

2014-02-26 08:33

JavaFX does not work in NetBeans7.4 using JDK7

It is easy to create JavaFX project using NetBeans7.4.
After updating JavaSE7 to 1.7.0_51, JavaFX project has ClassNotFoundException error in NetBeans7.4. It has no compiler errors.
So I just looked into the reason to improve my skill about Java.

f:id:tomoTaka:20140224073113p:image:w360

f:id:tomoTaka:20140224074516p:image:w360

f:id:tomoTaka:20140224073945p:image:medium

f:id:tomoTaka:20140224075039p:image:w360

Why has JavaFX project been compiled without error in NetBeans7.4

NetBeans adds [javafx.classpath.extension] to the library
f:id:tomoTaka:20140224074636p:image:w360

project.properties indicates location of javafx.classpath.extension.

...
javac.classpath=\
    ${javafx.classpath.extension}
...
javafx.classpath.extension=\
    ${platforms.JDK_1.7.home}/jre/lib/jfxrt.jar:\
    ${platforms.JDK_1.7.home}/jre/lib/javaws.jar:\
    ${platforms.JDK_1.7.home}/jre/lib/deploy.jar:\
    ${platforms.JDK_1.7.home}/jre/lib/plugin.jar

NetBeans does not add [javafx.classpath.extension] to the library
f:id:tomoTaka:20140224080626p:image:w360

The difference between JavaSE7 and JavaSE8.

This code shows how to get the locations of paths.
(Bootstrap classes, Extension classes and User classes)

import java.net.URL;

public class Test {
    public Test() {
        String pathProp = System.getProperty("sun.boot.class.path");
        String[] paths = pathProp.split(":");
        for (String path : paths) {
            System.out.println(String.format("path:%s", path));
        }
        System.out.println("**********");
        String extPathProp = System.getProperty("java.ext.dirs");
        String[] extPaths = extPathProp.split(":");
        for (String path : extPaths) {
            System.out.println(String.format("java.ext.dir:%s", path));
        }
        System.out.println("**********");
        String classPathProp = System.getProperty("java.class.path");
        String[] classPaths = classPathProp.split(":");
        for (String path : classPaths) {
            System.out.println(String.format("class.path:%s", path));
        }
        System.out.println("**********");
        ClassLoader loader = getClass().getClassLoader();
        URL url = loader.getResource("javafx/application/Application.class");
        System.out.println(String.format("Application URL:%s", url.toString()));
    }
    public static void main(String... args) {
        new Test();
    }
}
path:/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib/resources.jar
path:/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib/rt.jar
path:/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib/sunrsasign.jar
path:/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib/jsse.jar
path:/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib/jce.jar
path:/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib/charsets.jar
path:/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib/jfr.jar
path:/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/classes
**********
java.ext.dir:/Users/tomo/Library/Java/Extensions
java.ext.dir:/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib/ext
java.ext.dir:/Library/Java/Extensions
java.ext.dir:/Network/Library/Java/Extensions
java.ext.dir:/System/Library/Java/Extensions
java.ext.dir:/usr/lib/java
**********
class.path:.
**********
Exception in thread "main" java.lang.NullPointerException      // <---「javafx.application.Application.class」がloadできない?
	at Test.<init>(Test.java:25)
	at Test.main(Test.java:28)
path:/Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/jre/lib/resources.jar
path:/Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/jre/lib/rt.jar
path:/Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/jre/lib/sunrsasign.jar
path:/Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/jre/lib/jsse.jar
path:/Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/jre/lib/jce.jar
path:/Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/jre/lib/charsets.jar
path:/Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/jre/lib/jfr.jar
path:/Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/jre/classes
**********
java.ext.dir:/Users/tomo/Library/Java/Extensions
java.ext.dir:/Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/jre/lib/ext
java.ext.dir:/Library/Java/Extensions
java.ext.dir:/Network/Library/Java/Extensions
java.ext.dir:/System/Library/Java/Extensions
java.ext.dir:/usr/lib/java
**********
class.path:.
**********
Application URL:jar:file:/Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/jre/lib/ext/jfxrt.jar!/javafx/application/Application.class

JavaSE7 does not include jfxrt.jar in sun.boot.class.path.


versionlocation
JavaSE7/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib
JavaSE8/Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/jre/lib/ext
javac -cp /Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib/jfxrt.jar:. Test.java
java -cp /Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib/jfxrt.jar:. Test

The NullPointerException error resolves.

path:/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib/resources.jar
path:/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib/rt.jar
path:/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib/sunrsasign.jar
path:/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib/jsse.jar
path:/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib/jce.jar
path:/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib/charsets.jar
path:/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib/jfr.jar
path:/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/classes
**********
java.ext.dir:/Users/tomo/Library/Java/Extensions
java.ext.dir:/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib/ext
java.ext.dir:/Library/Java/Extensions
java.ext.dir:/Network/Library/Java/Extensions
java.ext.dir:/System/Library/Java/Extensions
java.ext.dir:/usr/lib/java
**********
class.path:/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib/jfxrt.jar
class.path:.
**********
Application URL:jar:file:/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib/jfxrt.jar!/javafx/application/Application.class

The conclusion

It is nothing to say,
I know I have no knowledge about classloader, so I must learn hard about Java.

just keep coding…;-)

2014-02-25 08:02

JavaSE7でのJavaFXの実行時エラーについて調べてみた

NetBeans7.4を使って、JavaFXプロジェクト(プラットフォームにJavaSE7)を作成。コンパイルできるのに、実行時に「java.lang.ClassNotFoundException: javafx.application.Application」エラーになるのでちょっと調べてみたことをメモ。

f:id:tomoTaka:20140224073113p:image:w360

f:id:tomoTaka:20140224074516p:image:w360

f:id:tomoTaka:20140224073945p:image:medium

コマンドラインで「javac」を使ってコンパイルした場合は、コンパイルエラーになることを確認。
f:id:tomoTaka:20140224075039p:image:w360

NetBeansコンパイルエラーにならない理由を調べる。

JavaSE7をプラットフォームに指定するとコンパイル時のライブラリーに「javafx.classpath.extension」が追加されている?
f:id:tomoTaka:20140224074636p:image:w360

javafx.classpath.extension」がどこを指しているのかは、「AppName/nbproject」配下の「project.properties」で以下のように設定されていた

...
javac.classpath=\
    ${javafx.classpath.extension}
...
javafx.classpath.extension=\
    ${platforms.JDK_1.7.home}/jre/lib/jfxrt.jar:\
    ${platforms.JDK_1.7.home}/jre/lib/javaws.jar:\
    ${platforms.JDK_1.7.home}/jre/lib/deploy.jar:\
    ${platforms.JDK_1.7.home}/jre/lib/plugin.jar

JavaSE8をプラットフォームに指定するとコンパイル時のライブラリーに「javafx.classpath.extension」の指定はない?
f:id:tomoTaka:20140224080626p:image:w360

JavaSE7とJavaSE8の違いを調べてみる。

以下の実装をJavaSE7とJavaSE8を使って実行。
ブートストラップクラス、拡張機能クラス、ユーザクラスを表示

import java.net.URL;

public class Test {
    public Test() {
        String pathProp = System.getProperty("sun.boot.class.path");
        String[] paths = pathProp.split(":");
        for (String path : paths) {
            System.out.println(String.format("path:%s", path));
        }
        System.out.println("**********");
        String extPathProp = System.getProperty("java.ext.dirs");
        String[] extPaths = extPathProp.split(":");
        for (String path : extPaths) {
            System.out.println(String.format("java.ext.dir:%s", path));
        }
        System.out.println("**********");
        String classPathProp = System.getProperty("java.class.path");
        String[] classPaths = classPathProp.split(":");
        for (String path : classPaths) {
            System.out.println(String.format("class.path:%s", path));
        }
        System.out.println("**********");
        ClassLoader loader = getClass().getClassLoader();
        URL url = loader.getResource("javafx/application/Application.class");   // <--- JavaSE7ではここでエラー
        System.out.println(String.format("Application URL:%s", url.toString()));
    }
    public static void main(String... args) {
        new Test();
    }
}
path:/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib/resources.jar
path:/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib/rt.jar
path:/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib/sunrsasign.jar
path:/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib/jsse.jar
path:/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib/jce.jar
path:/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib/charsets.jar
path:/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib/jfr.jar
path:/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/classes
**********
java.ext.dir:/Users/tomo/Library/Java/Extensions
java.ext.dir:/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib/ext
java.ext.dir:/Library/Java/Extensions
java.ext.dir:/Network/Library/Java/Extensions
java.ext.dir:/System/Library/Java/Extensions
java.ext.dir:/usr/lib/java
**********
class.path:.
**********
Exception in thread "main" java.lang.NullPointerException      // <---「javafx.application.Application.class」がloadできない?
	at Test.<init>(Test.java:25)
	at Test.main(Test.java:28)
path:/Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/jre/lib/resources.jar
path:/Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/jre/lib/rt.jar
path:/Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/jre/lib/sunrsasign.jar
path:/Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/jre/lib/jsse.jar
path:/Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/jre/lib/jce.jar
path:/Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/jre/lib/charsets.jar
path:/Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/jre/lib/jfr.jar
path:/Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/jre/classes
**********
java.ext.dir:/Users/tomo/Library/Java/Extensions
java.ext.dir:/Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/jre/lib/ext
java.ext.dir:/Library/Java/Extensions
java.ext.dir:/Network/Library/Java/Extensions
java.ext.dir:/System/Library/Java/Extensions
java.ext.dir:/usr/lib/java
**********
class.path:.
**********
Application URL:jar:file:/Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/jre/lib/ext/jfxrt.jar!/javafx/application/Application.class

JavaSE7では「jfxrt.jar」は「jre/lib」配下にあるのに「sun.boot.class.path」に含まれていない


version location
JavaSE7/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib
JavaSE8/Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/jre/lib/ext
javac -cp /Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib/jfxrt.jar:. Test.java
java -cp /Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib/jfxrt.jar:. Test

当然ですが、「NullPointerException」は解消

path:/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib/resources.jar
path:/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib/rt.jar
path:/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib/sunrsasign.jar
path:/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib/jsse.jar
path:/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib/jce.jar
path:/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib/charsets.jar
path:/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib/jfr.jar
path:/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/classes
**********
java.ext.dir:/Users/tomo/Library/Java/Extensions
java.ext.dir:/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib/ext
java.ext.dir:/Library/Java/Extensions
java.ext.dir:/Network/Library/Java/Extensions
java.ext.dir:/System/Library/Java/Extensions
java.ext.dir:/usr/lib/java
**********
class.path:/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib/jfxrt.jar
class.path:.
**********
Application URL:jar:file:/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib/jfxrt.jar!/javafx/application/Application.class

最後に

今更ながら、まだまだ勉強不足でクラスロードのことなど、問題に直面して初めて調べてみて、、、(汗)
NetBeansでのbuildはまだ調べてないです、、、

2013-12-05 08:18

File Tree View Sample(Part3) Drag and Drop

This is the 2nd day of the JavaFX Advent calendar.JavaFX Advent Calendar 2013 - Adventar
The 1st day is 曲線のアニメーション - JavaFX in the Box by Mr.@
The 3rd day is JavaFXでめくるエフェクト!! | るーつにゃんブログ by Mr.
@

I added the following one function to the previous sample application.File Tree View Sample(Part 2) - tomoTakaの日記

  1. Copying a file by a drag-and-drop operation on TreeView.

Thanks to this article Drag-and-Drop Feature in JavaFX Applications | JavaFX 2 Tutorials and Documentation ,it is not difficult to implement this function.

I used JavaSE8 for this sample application.
f:id:tomoTaka:20131130204615p:image:w360

f:id:tomoTaka:20131130202841p:image:w360

f:id:tomoTaka:20131130204247p:image:w280

f:id:tomoTaka:20131204074304p:image:w360

f:id:tomoTaka:20131201110225p:image:w360

        cell.setOnDragDetected(event -> {
            TreeItem<PathItem> item = cell.getTreeItem();
            if (item != null && item.isLeaf()) {
                Dragboard db = cell.startDragAndDrop(TransferMode.COPY);
                ClipboardContent content = new ClipboardContent();
                List<File> files = Arrays.asList(cell.getTreeItem().getValue().getPath().toFile());
                content.putFiles(files);
                db.setContent(content);
                event.consume();
            }
        });

        cell.setOnDragOver(event -> {
            TreeItem<PathItem> item = cell.getTreeItem();
            if ((item != null && !item.isLeaf()) &&
                    event.getGestureSource() != cell &&
                    event.getDragboard().hasFiles()) {
                Path targetPath = cell.getTreeItem().getValue().getPath();
                PathTreeCell sourceCell = (PathTreeCell) event.getGestureSource();
                final Path sourceParentPath = sourceCell.getTreeItem().getValue().getPath().getParent();
                if (sourceParentPath.compareTo(targetPath) != 0) {
                    event.acceptTransferModes(TransferMode.COPY);
                }
            }
            event.consume();
        });

        cell.setOnDragEntered(event -> {
            TreeItem<PathItem> item = cell.getTreeItem();
            if ((item != null && !item.isLeaf()) &&
                    event.getGestureSource() != cell &&
                    event.getDragboard().hasFiles()) {
                Path targetPath = cell.getTreeItem().getValue().getPath();
                PathTreeCell sourceCell = (PathTreeCell) event.getGestureSource();
                final Path sourceParentPath = sourceCell.getTreeItem().getValue().getPath().getParent();
                if (sourceParentPath.compareTo(targetPath) != 0) {
                    cell.setStyle("-fx-background-color: powderblue;");
                }                
            }
            event.consume();
        });

        cell.setOnDragExited(event -> {
            cell.setStyle("-fx-background-color: white");
            event.consume();
        });

        cell.setOnDragDropped(event -> {
            Dragboard db = event.getDragboard();
            boolean success = false;
            if (db.hasFiles()) {
                final Path source = db.getFiles().get(0).toPath();
                final Path target = Paths.get(
                        cell.getTreeItem().getValue().getPath().toAbsolutePath().toString(),
                        source.getFileName().toString());
                if (Files.exists(target, LinkOption.NOFOLLOW_LINKS)) {                    
                    Platform.runLater(() -> {
                        BooleanProperty replaceProp = new SimpleBooleanProperty();
                        // the dialog shows up here
                        CopyModalDialog dialog = new CopyModalDialog(stage, replaceProp);
                        replaceProp.addListener((ObservableValue<? extends Boolean> ov, Boolean oldValue, Boolean newValue) -> {
                            if (newValue) {
                                // if OK button is clicked, copying of files starts
                                FileCopyTask task = new FileCopyTask(source, target);
                                service.submit(task);
                            }
                        });
                    });
                } else {
                    // Copying of files starts
                    FileCopyTask task = new FileCopyTask(source, target);
                    service.submit(task);
                    task.setOnSucceeded(value -> {
                        Platform.runLater(() -> {
                            // add the source item to the target directory as a leaf
                            TreeItem<PathItem> item = PathTreeItem.createNode(new PathItem(target));
                            cell.getTreeItem().getChildren().add(item);
                        });
                    });
                }
                success = true;
            }
            event.setDropCompleted(success);
            event.consume();
        });
public class FileCopyTask extends Task<Void> {
    private Path source;
    private Path target;

    public FileCopyTask(Path source, Path target) {
        this.source = source;
        this.target = target;
    }    
    @Override
    protected Void call() throws Exception {
        Files.copy(this.source, this.target, StandardCopyOption.REPLACE_EXISTING);
        return null;
    }
}
public class CopyModalDialog {    
    public CopyModalDialog(Stage owner, final BooleanProperty replaceProp) {
        final Stage dialog = new Stage(StageStyle.UTILITY);
        dialog.initOwner(owner);
        dialog.initModality(Modality.APPLICATION_MODAL);
        GridPane root = new GridPane();
        root.setPadding(new Insets(30));
        root.setHgap(5);
        root.setVgap(10);
        Label label = new Label("The item already exists in this location. Do you want to replace it?");
        Button okButton = new Button("OK");
        okButton.setOnAction(event -> {
            replaceProp.set(true);
            dialog.hide();
        });
        Button cancelButton = new Button("Cancel");
        cancelButton.setOnAction(event -> {
            replaceProp.set(false);
            dialog.hide();
        });
        root.add(label, 0, 0, 2, 1);
        root.addRow(1, okButton, cancelButton);
        dialog.setScene(new Scene(root));
        dialog.show();
    }
}

The whole code is heretomoTaka01/FileTreeViewSample ? GitHub
I learned JavaFX a lot from the articleJava技術最前線 - JavaFX 2ではじめる、GUI開発 第14回 非同期処理:ITpro written by Mr.@
keep coding...

2013-12-02 07:47

File Tree View Sample(Part3) Drag and Drop

2013年のJavaFX Advent Calendar 2013 - Adventarの2日目です。in English here 2013-12-05 - tomoTakaの日記
前日は@さんの曲線のアニメーション - JavaFX in the Boxです。
明日は@さんのJavaFXでめくるエフェクト!! | るーつにゃんブログです。
JavaFXで作成中のFile Tree ViewFile Tree View Sample(Part 2) - tomoTakaの日記に以下の1機能を追加したので、その実装について書きました。

この記事 Drag-and-Drop Feature in JavaFX Applications | JavaFX 2 Tutorials and Documentationを参考に実装してみました。JavaFXでは、Drag and Dropがとても簡単に実装できます。

f:id:tomoTaka:20131130204615p:image:w360

f:id:tomoTaka:20131130202841p:image:w360

ここでコピー元のファイル名「t2.txt」の表示が消えるのは?です。
f:id:tomoTaka:20131130204247p:image:w280

f:id:tomoTaka:20131130203020p:image:w360

f:id:tomoTaka:20131201110225p:image:w360

・ドラッグ元のセルの「setOnDragDetected」メソッドで

  1. ファイルのみを対象にしたいので「isLeaf」で判定
  2. ドラッグ処理として「コピー」を設定
  3. ドラッグ元のコンテントである「File」を「Dragboard」に設定

(ClipboardContentにPathを設定するメソッドがなかったのでファイルに変換)

        cell.setOnDragDetected(event -> {
            TreeItem<PathItem> item = cell.getTreeItem();
            if (item != null && item.isLeaf()) {
                Dragboard db = cell.startDragAndDrop(TransferMode.COPY);
                ClipboardContent content = new ClipboardContent();
                List<File> files = Arrays.asList(cell.getTreeItem().getValue().getPath().toFile());
                content.putFiles(files);
                db.setContent(content);
                event.consume();
            }
        });

・ドラッグ先のセルの「setOnDragOver」メソッドで

  1. ドラッグ先としてここでは、ディレクトリを対象にしたいので「!isLeaf」で判定
  2. ドラッグ元と同じセルは対象外
  3. ドラッグ先がドラッグ中ファイルのディレクトリも対象外(画面4の場合)
  4. ドラッグ処理として「コピー」を設定

(moveも設定したかったのですが、moveイベントが「null」になるようなので、また後日調べる予定です)

        cell.setOnDragOver(event -> {
            TreeItem<PathItem> item = cell.getTreeItem();
            if ((item != null && !item.isLeaf()) &&
                    event.getGestureSource() != cell &&
                    event.getDragboard().hasFiles()) {
                Path targetPath = cell.getTreeItem().getValue().getPath();
                PathTreeCell sourceCell = (PathTreeCell) event.getGestureSource();
                final Path sourceParentPath = sourceCell.getTreeItem().getValue().getPath().getParent();
                if (sourceParentPath.compareTo(targetPath) != 0) {
                    // ドラッグ先がドラッグ中ファイルのディレクトリでない場合
                    event.acceptTransferModes(TransferMode.COPY);
                }
            }
            event.consume();
        });

・ドラッグ先のセルの「setOnDragEntered」メソッドで

  1. ドラッグ先は、code2と同じ条件で設定
  2. ドラッグ先にマウスがあわさった時(まだドラッグ中)にセルの背景色をブルーにする
  3. 画面2のようにディレクトリ上にマウスがある時に背景色を変更する処理はここで実装
        cell.setOnDragEntered(event -> {
            TreeItem<PathItem> item = cell.getTreeItem();
            if ((item != null && !item.isLeaf()) &&
                    event.getGestureSource() != cell &&
                    event.getDragboard().hasFiles()) {
                Path targetPath = cell.getTreeItem().getValue().getPath();
                PathTreeCell sourceCell = (PathTreeCell) event.getGestureSource();
                final Path sourceParentPath = sourceCell.getTreeItem().getValue().getPath().getParent();
                if (sourceParentPath.compareTo(targetPath) != 0) {
                    // ドラッグ先がドラッグ中ファイルのディレクトリでない場合
                    cell.setStyle("-fx-background-color: powderblue;");
                }                
            }
            event.consume();
        });

・ドラッグ先のセルの「setOnDragExited」メソッドで

  1. code3で背景色をブルーにしていたので、マウスがでていった時に元の背景色に戻す。
        cell.setOnDragExited(event -> {
            cell.setStyle("-fx-background-color: white");
            event.consume();
        });

・ドラッグ先のセルの「setOnDragDropped」メソッドで、実際のドラッグ処理を実装

  1. コピー先に同じファイルが存在する場合、上書きするかを確認するためのダイアログ画面を表示(ダイアログ画面の実装は「code7」参照)
  2. 「ok」「cancel」どちらのボタンがクリックされたかは、「replaceProp(BooleanProperty)」に設定
  3. ファイルのコピー処理を非同期に実行(ファイルのコピー処理は「code6」参照)
  4. コピーしたファイルをTree Viewに表示(上書き処理した場合はすでに表示されているので不要)
        cell.setOnDragDropped(event -> {
            Dragboard db = event.getDragboard();
            boolean success = false;
            if (db.hasFiles()) {
                final Path source = db.getFiles().get(0).toPath();
                final Path target = Paths.get(
                        cell.getTreeItem().getValue().getPath().toAbsolutePath().toString(),
                        source.getFileName().toString());
                if (Files.exists(target, LinkOption.NOFOLLOW_LINKS)) {                    
                    Platform.runLater(() -> {
                        BooleanProperty replaceProp = new SimpleBooleanProperty();
                        // 上書き確認のダイアログ表示
                        CopyModalDialog dialog = new CopyModalDialog(stage, replaceProp);
                        replaceProp.addListener((ObservableValue<? extends Boolean> ov, Boolean oldValue, Boolean newValue) -> {
                            if (newValue) {
                                // ダイアログ画面で「OK」がクリックされた場合、ファイルのコピー処理を非同期に実施
                                FileCopyTask task = new FileCopyTask(source, target);
                                service.submit(task);
                            }
                        });
                    });
                } else {
                    // コピー先に同じファイルが存在しない場合、ファイルのコピー処理を非同期に実施
                    FileCopyTask task = new FileCopyTask(source, target);
                    service.submit(task);
                    task.setOnSucceeded(value -> {
                        Platform.runLater(() -> {
                            // Tree Viewのコピー先ディレクトリ配下にコピーしたファイルを追加
                            TreeItem<PathItem> item = PathTreeItem.createNode(new PathItem(target));
                            cell.getTreeItem().getChildren().add(item);
                        });
                    });
                }
                success = true;
            }
            event.setDropCompleted(success);
            event.consume();
        });

・ファイルのコピーを実施する処理をBackgroundで実行するために、「Task」クラスを拡張

public class FileCopyTask extends Task<Void> {
    private Path source;
    private Path target;

    public FileCopyTask(Path source, Path target) {
        this.source = source;
        this.target = target;
    }    
    @Override
    protected Void call() throws Exception {
        Files.copy(this.source, this.target, StandardCopyOption.REPLACE_EXISTING);
        return null;
    }
}

・コピー先に同じファイルが存在する場合、上書きするかを確認するためのダイアログ画面を表示
・「ok」「cancel」どちらのボタンがクリックされたかは、「replaceProp(BooleanProperty)」に設定

public class CopyModalDialog {    
    public CopyModalDialog(Stage owner, final BooleanProperty replaceProp) {
        final Stage dialog = new Stage(StageStyle.UTILITY);
        dialog.initOwner(owner);
        dialog.initModality(Modality.APPLICATION_MODAL);
        GridPane root = new GridPane();
        root.setPadding(new Insets(30));
        root.setHgap(5);
        root.setVgap(10);
        Label label = new Label("既に存在します。上書きしますか?");
        Button okButton = new Button("OK");
        okButton.setOnAction(event -> {
            replaceProp.set(true);
            dialog.hide();
        });
        Button cancelButton = new Button("Cancel");
        cancelButton.setOnAction(event -> {
            replaceProp.set(false);
            dialog.hide();
        });
        root.add(label, 0, 0, 2, 1);
        root.addRow(1, okButton, cancelButton);
        dialog.setScene(new Scene(root));
        dialog.show();
    }
}

コードはここtomoTaka01/FileTreeViewSample ? GitHubにアップしています。
ドラッグ処理中に上書きダイアログを表示するのに「Platform.runLater」を使用するなど、非同期処理はこの記事Java技術最前線 - JavaFX 2ではじめる、GUI開発 第14回 非同期処理:ITproで勉強しました!
今後もディレクトリのコピーなど、機能を追加していければと思っています。

次へ
tomoTaka
tomoTaka