Java linux 中的 TreeView 问题
Java TreeView issue in linux
我正在尝试在我正在开发的应用程序中创建一个文件管理器。这个文件管理器使用 TreeView 和 TreeItem,但我坚持获取根文件夹。
I hope this image let me explain better。
这是主要的 class:
public class JavaFXFileBrowseDemoApp extends Application {
private TreeView<String> treeView;
public static void main(String[] args){
launch(args);
}
@Override
public void start(Stage primaryStage){
//create tree pane
VBox treeBox=new VBox();
treeBox.setPadding(new Insets(10,10,10,10));
treeBox.setSpacing(10);
//setup the file browser root
String hostName="computer";
try{
hostName=InetAddress.getLocalHost().getHostName();
}
catch(UnknownHostException x){}
TreeItem<String> rootNode = new TreeItem<>(hostName, new ImageView(FilePathTreeItem.computer)); // FilePathTreeItem.computer is just an image
Iterable<Path> rootDirectories = FileSystems.getDefault().getRootDirectories();
for(Path name : rootDirectories){
System.out.println(name.getFileName()); // <----- it gives me null
FilePathTreeItem treeNode=new FilePathTreeItem(name);
rootNode.getChildren().add(treeNode);
}
rootNode.setExpanded(true);
//create the tree view
treeView=new TreeView<>(rootNode);
//add everything to the tree pane
treeBox.getChildren().addAll(new Label("File browser"),treeView);
VBox.setVgrow(treeView,Priority.ALWAYS);
//setup and show the window
primaryStage.setTitle("JavaFX File Browse Demo");
StackPane root=new StackPane();
root.getChildren().addAll(treeBox);
primaryStage.setScene(new Scene(root,400,300));
primaryStage.show();
}
}
这里有 FilePathTreeItem class:
public class FilePathTreeItem extends TreeItem<String>{
public static Image computer = new Image(ClassLoader.getSystemResourceAsStream("computer.png"));
public static Image folderClosed = new Image(ClassLoader.getSystemResourceAsStream("folder-closed.png"));
public static Image folderOpened = new Image(ClassLoader.getSystemResourceAsStream("folder-opened.png"));
public static Image genericText= new Image(ClassLoader.getSystemResourceAsStream("generic-text.png"));
private String fullPath;
private boolean isDirectory;
public FilePathTreeItem(Path file){
super(file.toString());
fullPath = file.toString();
// test if this is a directory and set the icon
if (Files.isDirectory(file)){
isDirectory = true;
setGraphic(new ImageView(folderClosed));
}
else{
isDirectory = false;
if (file.endsWith("txt")){
setGraphic(new ImageView(genericText));
}
}
if(!fullPath.endsWith(File.separator)){
String value = file.toString();
int indexOf = value.lastIndexOf(File.separator);
if (indexOf > 0){
setValue(value.substring(indexOf + 1));
}
else{
setValue(value);
}
}
this.addEventHandler(TreeItem.<Object>branchExpandedEvent(), new EventHandler(){
@Override
public void handle(Event e){
FilePathTreeItem source = (FilePathTreeItem)e.getSource();
if (source.isDirectory() && source.isExpanded()){
ImageView iv = (ImageView)source.getGraphic();
iv.setImage(folderOpened);
}
try{
if (source.getChildren().isEmpty()){
Path path = Paths.get(source.getFullPath());
BasicFileAttributes attribs = Files.readAttributes(path, BasicFileAttributes.class);
if (attribs.isDirectory()){
DirectoryStream<Path> dir = Files.newDirectoryStream(path);
for(Path file : dir){
FilePathTreeItem treeNode = new FilePathTreeItem(file);
source.getChildren().add(treeNode);
}
}
}
else{
//if you want to implement rescanning a directory for changes this would be the place to do it
}
}
catch(IOException x){
x.printStackTrace();
}
}
});
this.addEventHandler(TreeItem.<Object>branchCollapsedEvent(), new EventHandler(){
@Override
public void handle(Event e){
FilePathTreeItem source = (FilePathTreeItem) e.getSource();
if (source.isDirectory() && !source.isExpanded()){
ImageView iv = (ImageView) source.getGraphic();
iv.setImage(folderOpened);
}
}
});
}
public String getFullPath() {
return fullPath;
}
public boolean isDirectory() {
return isDirectory;
}
}
您的代码(实际上是您基于它的示例)不起作用,因为在树项展开之前没有子节点被添加到树项中。但是JavaFXTreeItem
如果是叶子节点就不能展开,叶子节点的定义是没有子节点的。所以你永远没有机会扩展节点,它也永远不会被填充。
您需要按照以下方式做一些事情:
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Collectors;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.TreeItem;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
public class FilePathTreeItem extends TreeItem<String>{
public static Image computer = new Image(ClassLoader.getSystemResourceAsStream("computer.png"));
public static Image folderClosed = new Image(ClassLoader.getSystemResourceAsStream("folder.png"));
public static Image folderOpened = new Image(ClassLoader.getSystemResourceAsStream("folder-open.png"));
public static Image genericText= new Image(ClassLoader.getSystemResourceAsStream("text-x-generic.png"));
private ImageView imageView = new ImageView();
private String fullPath;
private boolean isDirectory;
private boolean isFirstTimeLeaf = true ;
private boolean isFirstTimeChildren = true ;
private boolean isLeaf ;
public FilePathTreeItem(Path file){
super(file.toString());
fullPath = file.toString();
// test if this is a directory and set the icon
if (Files.isDirectory(file)){
isDirectory = true;
setGraphic(new ImageView(folderClosed));
}
else{
isDirectory = false;
if (file.endsWith("txt")){
setGraphic(new ImageView(genericText));
}
}
if(!fullPath.endsWith(File.separator)){
String value = file.toString();
int indexOf = value.lastIndexOf(File.separator);
if (indexOf > 0){
setValue(value.substring(indexOf + 1));
}
else{
setValue(value);
}
}
this.expandedProperty().addListener((obs, wasExpanded, isNowExpanded) -> {
if (Files.isDirectory(Paths.get(fullPath))) {
if (isNowExpanded) {
imageView.setImage(folderOpened);
} else {
imageView.setImage(folderClosed);
}
setGraphic(imageView);
}
});
}
@Override
public ObservableList<TreeItem<String>> getChildren() {
if (isFirstTimeChildren) {
isFirstTimeChildren = false ;
super.getChildren().setAll(buildChildren());
}
return super.getChildren() ;
}
@Override
public boolean isLeaf() {
if (isFirstTimeLeaf) {
isFirstTimeLeaf = false ;
Path path = Paths.get(fullPath);
isLeaf = ! Files.isDirectory(path);
}
return isLeaf ;
}
private ObservableList<TreeItem<String>> buildChildren() {
Path path = Paths.get(fullPath);
if (Files.isDirectory(path)) {
try {
return Files.list(path).map(FilePathTreeItem::new)
.collect(Collectors.toCollection(FXCollections::observableArrayList));
} catch (IOException e) {
e.printStackTrace();
}
}
return FXCollections.observableArrayList();
}
public String getFullPath() {
return fullPath;
}
public boolean isDirectory() {
return isDirectory;
}
}
虽然我不是特别喜欢他的方法,但我使它与您借用的示例非常接近。我确实清理了很多事件处理并更新了其中一些(使用 lambda 等)。但实际上 String
s 和 Path
s 之间仍然存在太多的转换:如果你正在构建一个树,其中每个节点基本上代表一个 Path
,那么你应该使用 TreeView<Path>
和 TreeItem<Path>
等,不会不断地转换字符串和路径之间的所有内容。
我正在尝试在我正在开发的应用程序中创建一个文件管理器。这个文件管理器使用 TreeView 和 TreeItem,但我坚持获取根文件夹。 I hope this image let me explain better。 这是主要的 class:
public class JavaFXFileBrowseDemoApp extends Application {
private TreeView<String> treeView;
public static void main(String[] args){
launch(args);
}
@Override
public void start(Stage primaryStage){
//create tree pane
VBox treeBox=new VBox();
treeBox.setPadding(new Insets(10,10,10,10));
treeBox.setSpacing(10);
//setup the file browser root
String hostName="computer";
try{
hostName=InetAddress.getLocalHost().getHostName();
}
catch(UnknownHostException x){}
TreeItem<String> rootNode = new TreeItem<>(hostName, new ImageView(FilePathTreeItem.computer)); // FilePathTreeItem.computer is just an image
Iterable<Path> rootDirectories = FileSystems.getDefault().getRootDirectories();
for(Path name : rootDirectories){
System.out.println(name.getFileName()); // <----- it gives me null
FilePathTreeItem treeNode=new FilePathTreeItem(name);
rootNode.getChildren().add(treeNode);
}
rootNode.setExpanded(true);
//create the tree view
treeView=new TreeView<>(rootNode);
//add everything to the tree pane
treeBox.getChildren().addAll(new Label("File browser"),treeView);
VBox.setVgrow(treeView,Priority.ALWAYS);
//setup and show the window
primaryStage.setTitle("JavaFX File Browse Demo");
StackPane root=new StackPane();
root.getChildren().addAll(treeBox);
primaryStage.setScene(new Scene(root,400,300));
primaryStage.show();
}
}
这里有 FilePathTreeItem class:
public class FilePathTreeItem extends TreeItem<String>{
public static Image computer = new Image(ClassLoader.getSystemResourceAsStream("computer.png"));
public static Image folderClosed = new Image(ClassLoader.getSystemResourceAsStream("folder-closed.png"));
public static Image folderOpened = new Image(ClassLoader.getSystemResourceAsStream("folder-opened.png"));
public static Image genericText= new Image(ClassLoader.getSystemResourceAsStream("generic-text.png"));
private String fullPath;
private boolean isDirectory;
public FilePathTreeItem(Path file){
super(file.toString());
fullPath = file.toString();
// test if this is a directory and set the icon
if (Files.isDirectory(file)){
isDirectory = true;
setGraphic(new ImageView(folderClosed));
}
else{
isDirectory = false;
if (file.endsWith("txt")){
setGraphic(new ImageView(genericText));
}
}
if(!fullPath.endsWith(File.separator)){
String value = file.toString();
int indexOf = value.lastIndexOf(File.separator);
if (indexOf > 0){
setValue(value.substring(indexOf + 1));
}
else{
setValue(value);
}
}
this.addEventHandler(TreeItem.<Object>branchExpandedEvent(), new EventHandler(){
@Override
public void handle(Event e){
FilePathTreeItem source = (FilePathTreeItem)e.getSource();
if (source.isDirectory() && source.isExpanded()){
ImageView iv = (ImageView)source.getGraphic();
iv.setImage(folderOpened);
}
try{
if (source.getChildren().isEmpty()){
Path path = Paths.get(source.getFullPath());
BasicFileAttributes attribs = Files.readAttributes(path, BasicFileAttributes.class);
if (attribs.isDirectory()){
DirectoryStream<Path> dir = Files.newDirectoryStream(path);
for(Path file : dir){
FilePathTreeItem treeNode = new FilePathTreeItem(file);
source.getChildren().add(treeNode);
}
}
}
else{
//if you want to implement rescanning a directory for changes this would be the place to do it
}
}
catch(IOException x){
x.printStackTrace();
}
}
});
this.addEventHandler(TreeItem.<Object>branchCollapsedEvent(), new EventHandler(){
@Override
public void handle(Event e){
FilePathTreeItem source = (FilePathTreeItem) e.getSource();
if (source.isDirectory() && !source.isExpanded()){
ImageView iv = (ImageView) source.getGraphic();
iv.setImage(folderOpened);
}
}
});
}
public String getFullPath() {
return fullPath;
}
public boolean isDirectory() {
return isDirectory;
}
}
您的代码(实际上是您基于它的示例)不起作用,因为在树项展开之前没有子节点被添加到树项中。但是JavaFXTreeItem
如果是叶子节点就不能展开,叶子节点的定义是没有子节点的。所以你永远没有机会扩展节点,它也永远不会被填充。
您需要按照以下方式做一些事情:
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Collectors;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.TreeItem;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
public class FilePathTreeItem extends TreeItem<String>{
public static Image computer = new Image(ClassLoader.getSystemResourceAsStream("computer.png"));
public static Image folderClosed = new Image(ClassLoader.getSystemResourceAsStream("folder.png"));
public static Image folderOpened = new Image(ClassLoader.getSystemResourceAsStream("folder-open.png"));
public static Image genericText= new Image(ClassLoader.getSystemResourceAsStream("text-x-generic.png"));
private ImageView imageView = new ImageView();
private String fullPath;
private boolean isDirectory;
private boolean isFirstTimeLeaf = true ;
private boolean isFirstTimeChildren = true ;
private boolean isLeaf ;
public FilePathTreeItem(Path file){
super(file.toString());
fullPath = file.toString();
// test if this is a directory and set the icon
if (Files.isDirectory(file)){
isDirectory = true;
setGraphic(new ImageView(folderClosed));
}
else{
isDirectory = false;
if (file.endsWith("txt")){
setGraphic(new ImageView(genericText));
}
}
if(!fullPath.endsWith(File.separator)){
String value = file.toString();
int indexOf = value.lastIndexOf(File.separator);
if (indexOf > 0){
setValue(value.substring(indexOf + 1));
}
else{
setValue(value);
}
}
this.expandedProperty().addListener((obs, wasExpanded, isNowExpanded) -> {
if (Files.isDirectory(Paths.get(fullPath))) {
if (isNowExpanded) {
imageView.setImage(folderOpened);
} else {
imageView.setImage(folderClosed);
}
setGraphic(imageView);
}
});
}
@Override
public ObservableList<TreeItem<String>> getChildren() {
if (isFirstTimeChildren) {
isFirstTimeChildren = false ;
super.getChildren().setAll(buildChildren());
}
return super.getChildren() ;
}
@Override
public boolean isLeaf() {
if (isFirstTimeLeaf) {
isFirstTimeLeaf = false ;
Path path = Paths.get(fullPath);
isLeaf = ! Files.isDirectory(path);
}
return isLeaf ;
}
private ObservableList<TreeItem<String>> buildChildren() {
Path path = Paths.get(fullPath);
if (Files.isDirectory(path)) {
try {
return Files.list(path).map(FilePathTreeItem::new)
.collect(Collectors.toCollection(FXCollections::observableArrayList));
} catch (IOException e) {
e.printStackTrace();
}
}
return FXCollections.observableArrayList();
}
public String getFullPath() {
return fullPath;
}
public boolean isDirectory() {
return isDirectory;
}
}
虽然我不是特别喜欢他的方法,但我使它与您借用的示例非常接近。我确实清理了很多事件处理并更新了其中一些(使用 lambda 等)。但实际上 String
s 和 Path
s 之间仍然存在太多的转换:如果你正在构建一个树,其中每个节点基本上代表一个 Path
,那么你应该使用 TreeView<Path>
和 TreeItem<Path>
等,不会不断地转换字符串和路径之间的所有内容。