我正在尝试开发一个使用gui从系统中的任何位置移动文件的小应用程序。我有移动文件的代码,他们确实在选择和按下按钮时移动,但我不知道如何刷新文件系统查看器以反映更改。我必须设置系统查看器的代码如下:
public class FileMover {
//Start of Global Variables
private JTree tree;
private DefaultTreeModel treeModel;
private FileSystemView fileSystemView;
protected File currentFile;
protected LinkedList fileLocations;
protected JTree movedTree;
protected JPanel areaLeft;
protected JPanel areaRight;
protected JPanel areaMiddle;
protected final JFrame openFrame;
//end of global variables.
//Constructor for FileMover
public FileMover()
{
openFrame = new JFrame("File Mover");
createFileMover();
}
public void createFileMover(){
Container contentPane = this.openFrame.getContentPane();
fileLocations = new LinkedList();
contentPane.setLayout(new BorderLayout());
areaLeft = new JPanel();
areaRight = new JPanel();
areaMiddle = new JPanel();
contentPane.add(areaLeft, BorderLayout.WEST);
contentPane.add(areaRight, BorderLayout.EAST);
contentPane.add(areaMiddle, BorderLayout.CENTER);
areaLeft.add(createSystemView());
movedTree = new JTree(fileLocations.toArray());
JScrollPane movedPane = new JScrollPane(movedTree);
JButton moveRightButton = new JButton("->");
JButton moveLeftButton = new JButton("<-");
JButton refresh = new JButton("Refresh");
areaMiddle.setLayout(new GridLayout(1,2));
areaMiddle.add(moveRightButton);
areaMiddle.add(refresh);
areaMiddle.add(moveLeftButton);
//actualy move the file
moveRightButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
System.out.println("Moving file: "+ currentFile.getName());
fileLocations.add(currentFile);
try {
//move the file to the correct location.
moveFile(currentFile);
} catch (IOException ex) {
Logger.getLogger(FileMover.class.getName()).log(Level.SEVERE, null, ex);
}
System.out.println(fileLocations.getFirst().toString());
}
});
//refresh the gui
refresh.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
refresh();
}
});
//finish setting up the frame
openFrame.setSize(1280, 768);
openFrame.setLocationRelativeTo(null);
openFrame.setDefaultCloseOperation(3);
openFrame.setResizable(false);
openFrame.pack();
openFrame.setVisible(true);
}
/** Add the files that are contained within the directory of this node.
*/
private void showChildren(final DefaultMutableTreeNode node) {
tree.setEnabled(false);
SwingWorker<Void, File> worker = new SwingWorker<Void, File>() {
@Override
public Void doInBackground() {
File file = (File) node.getUserObject();
if (file.isDirectory()) {
File[] files = fileSystemView.getFiles(file, true); //!!
if (node.isLeaf()) {
for (File child : files) {
publish(child);
}
}
}
return null;
}
@Override
protected void process(List<File> chunks) {
for (File child : chunks) {
node.add(new DefaultMutableTreeNode(child));
}
}
@Override
protected void done() {
tree.setEnabled(true);
}
};
worker.execute();
}
/** Update the File details view with the details of this File. */
private void setFileDetails(File file) {
System.out.println("Path: "+ file.getPath());
System.out.println("Name: "+ fileSystemView.getSystemDisplayName(file));
}
private void refresh(){
//refresh the tree here
}
private JScrollPane createSystemView(){
//file syatem hierarchy
fileSystemView = FileSystemView.getFileSystemView();
// the File tree
DefaultMutableTreeNode root = new DefaultMutableTreeNode();
treeModel = new DefaultTreeModel(root);
TreeSelectionListener treeSelectionListener = new TreeSelectionListener() {
@Override
public void valueChanged(TreeSelectionEvent tse){
DefaultMutableTreeNode node =
(DefaultMutableTreeNode)tse.getPath().getLastPathComponent();
showChildren(node);
setFileDetails((File)node.getUserObject());
currentFile = (File)node.getUserObject();
}
};
// show the file system roots.
File[] roots = fileSystemView.getRoots();
for (File fileSystemRoot : roots) {
DefaultMutableTreeNode node = new DefaultMutableTreeNode(fileSystemRoot);
root.add( node );
File[] files = fileSystemView.getFiles(fileSystemRoot, true);
for (File file : files) {
if (file.isDirectory()) {
node.add(new DefaultMutableTreeNode(file));
}
}
}
tree = new JTree(treeModel);
tree.setRootVisible(false);
tree.addTreeSelectionListener(treeSelectionListener);
tree.setCellRenderer(new FileTreeCellRenderer());
tree.expandRow(0);
JScrollPane treeScroll = new JScrollPane(tree);
tree.setVisibleRowCount(15);
Dimension preferredSize = treeScroll.getPreferredSize();
Dimension widePreferred = new Dimension(
200,
(int)preferredSize.getHeight());
treeScroll.setPreferredSize( widePreferred );
return treeScroll;
}
左移按钮和区域右边没有完成,但我需要的是当我在树中选择一个节点并单击右箭头按钮时,节点反映的文件/文件夹由我的moveFile代码在内部移动。但是这个变化没有反映在树中,那么如何在树中显示这种变化,即刷新树以显示文件系统的当前状态?
我尝试过treeModel.reload();但这似乎不起作用,并抛出一个空指针异常。
我试过了:
areaLeft.removeAll();
areaLeft.add(createSystemView());
认为它可以通过重新创建系统视图来刷新它,但似乎没有做任何事情。
这里的帮助将非常感谢!
编辑:下面是文件树渲染器的请求代码:
/** A TreeCellRenderer for a File. */
class FileTreeCellRenderer extends DefaultTreeCellRenderer {
private static final long serialVersionUID = -7799441088157759804L;
private FileSystemView fileSystemView;
private JLabel label;
FileTreeCellRenderer() {
label = new JLabel();
label.setOpaque(true);
fileSystemView = FileSystemView.getFileSystemView();
}
@Override
public Component getTreeCellRendererComponent(
JTree tree,
Object value,
boolean selected,
boolean expanded,
boolean leaf,
int row,
boolean hasFocus) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode)value;
File file = (File)node.getUserObject();
label.setIcon(fileSystemView.getSystemIcon(file));
label.setText(fileSystemView.getSystemDisplayName(file));
label.setToolTipText(file.getPath());
if (selected) {
label.setBackground(backgroundSelectionColor);
label.setForeground(textSelectionColor);
} else {
label.setBackground(backgroundNonSelectionColor);
label.setForeground(textNonSelectionColor);
}
return label;
}
}
答案 0 :(得分:1)
因为从您的代码中看起来您知道自己在做什么,所以我只会展示仅在第一次工作的基本示例您按下刷新按钮:
private DefaultMutableTreeNode someNode;
private void refresh(){
System.out.println(someNode);
treeModel.removeNodeFromParent(someNode);
}
并像这样重写createSystemView()的一部分:
int cnt = 0;
for (File file : files) {
if (file.isDirectory()) {
if ((cnt++) == 5) { //1
System.out.println(file.getPath());
node.add(someNode = new DefaultMutableTreeNode(file));
}
else {
node.add(new DefaultMutableTreeNode(file));
}
}
}
只有在根目录中至少有六个(注释1)目录时,才能使用此功能。运行该文件,从root计算目录 - 当您按下刷新按钮时,它将删除第六个目录。如果再次按下该按钮,它将尝试删除已删除的节点,因此您将收到IllegalArgumentException。
您需要致电removeNodeFromParent上的treeModel:
发送消息以从其父节点中删除节点。这将消息nodesWereRemoved创建适当的事件。这是在为您处理事件创建时删除节点的首选方法。
另见example。
如果要刷新整个视图,您应该像在初始化中那样在referh函数中重新创建模型,或者只是迭代模型并根据需要进行更新 - 但我建议模型的breadth first traversal在那种情况下。
编辑:
“因此,每次移动后刷新都会再次显示FS,并显示现在应该处于的文件”。让我们从重新初始化模型开始,我只是指DefaultTreeModel类的treeModel实例。
因此,在refresh()方法中,您创建一个新的treeModel实例,并像在createSystemView()方法中一样用DefaultMutableTreeNode实例填充它:
DefaultMutableTreeNode root = new DefaultMutableTreeNode();
treeModel = new DefaultTreeModel(root);
File[] roots = fileSystemView.getRoots();
for (File fileSystemRoot : roots) {
DefaultMutableTreeNode node = new DefaultMutableTreeNode(fileSystemRoot);
root.add( node );
File[] files = fileSystemView.getFiles(fileSystemRoot, true);
for (File file : files) {
if (file.isDirectory()) {
node.add(new DefaultMutableTreeNode(file));
}
}
}
我们准备了模型,现在我们需要将它设置为树,我相信仅仅是:
tree.setModel(treeModel);
应该足够了,注意你不需要添加监听器,因为setModel方法将侦听器从旧模型重新附加到新模型,并且还通知JTree视图相应地重绘自己 - 我检查了源。如果它不会重绘(我怀疑它,但我没有测试过)强制它在下一行,如下所示:
treeModel.reload();
用于reload()方法here的APIdoc。
但是如果你想在刷新函数中更新模型,我认为对于一个名为“refresh”的操作更自然的操作,你需要获取新的第一级目录,然后像这样遍历树(因为我们遍历所有root的孩子,这将是所提到的广度优先遍历):
int firstLevelCount = treeModel.getChildCount(root);
DefaultMutableTreeNode child;
for (int i=0; i < firstLevelCount; i++) {
child = treeModel.getChild(root, index);
// update logic part 1
}
// update logic part 2
treeModel.reload();
您将需要DefaultMutableTreeNode.getUserObject()方法,该方法将返回该树节点所代表的文件。
显然,您将要删除模型中不在新文件数组中的所有节点,并且对于模型中没有其对应节点的所有文件,您需要添加它们到模特。
我建议不要使用文件数组但是列表(以便您可以从List.contains()方法中受益),例如。
List<File> files = new ArrayList<File>(Arrays.asList(FileSystemView.getFileSystemView().getRoots()));
另外,像这样重写一部分渲染器:
if (file != null) {
label.setIcon(fileSystemView.getSystemIcon(file));
label.setText(fileSystemView.getSystemDisplayName(file));
label.setToolTipText(file.getPath());
}
因为视图中的根节点没有关联文件,因此您很可能在某个时候更新模型时获得NPE。
在我描述的第二个变体中,您可能会想要从循环内部删除根子节点(在更新逻辑1部分) - 如果这样做,您很可能会获得ConcurrentModificationException。解决方案是制作另一个
List<DefaultMutableTreeNode> toBeRemoved = new ArrayList<DefaultMutableTreeNode>();
并在//更新逻辑第1部分(插入循环)而不是从模型中删除节点,将其放入该列表中。当您完成迭代循环时,您只需遍历该列表并将其从//更新逻辑第2部分的模型中删除,例如。
for (DefaultMutableTreeNode node : toBeRemoved) {
treeModel.removeNodeFromParent(node);
}
如上所述,这将自动触发视图(JTree)重绘,因此请查看最符合您需求的内容。
EDIT ^ 2:
关于刷新方法的第二个变体,您已经拥有
private DefaultTreeModel treeModel;
作为全局变量,您可以像这样(getRoot())
获取树的根DefaultMutableTreeNode root = (DefaultMutableTreeNode)treeModel.getRoot();
正如您所做的那样,您将获得文件系统的当前状态,如下所示:
FileSystemView.getFileSystemView().getRoots();
这些是编写所述refresh()方法所需的所有变量。
关于移动(带箭头的按钮(&lt; - , - &gt;))操作,从treeSelectionListener内部将此变量设为全局:
DefaultMutableTreeNode node = (DefaultMutableTreeNode)tse.getPath().getLastPathComponent();
使用此变量(让我们称之为selectedNode),您可以使用DefaultTreeModel方法:
请注意,这两种方法都会触发JTree重绘。这应该足以实现您描述的操作。此外,您可以像这样重写moveButton actionListener方法:
moveRightButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
if (selectedNode != null) {
//all the move-code-refresh-tre-view-here
selectedNode = null;
}
}
});
selectedNode只能由treeSelectionListener设置,因此以这种方式确保只有在树视图中实际选择了文件时才会进行文件移动操作。