`
tempsitegoogle
  • 浏览: 862165 次
文章分类
社区版块
存档分类
最新评论

弹出窗口与选择器(五)

 
阅读更多

9.5 JFileChooser类

Swing组件集合同时提供了用于选择文件名字与目录的选择器:JFileChooser类。这个选择器替换了原始AWT组件集合中使用FileDialog的需要。类似于其他的Swing选择器组件,JFileChooser并没有自动被放入一个弹出窗口中,但是他可以放在我们程序中用户界面的任何地方。图9-25显示了一个具有Metal观感,Ocean主题的JFileChooser,他被自动放在一个模态JDialog中。


对JFileChooser提供支持的是javax.swing.filechooser包中的大量类。这些支持类包括用于限制列出在JFileChooser的FileView的文件与目录。FileView控制目录与文件如何列在JFileChooser中。FileSystemView是一个尝试由选择器隐藏文件系统相关的操作系统细节的一个抽象类。Java 2平台提供者将会提供特定操作系统的版本,从而类似列出根分区这样的任务可以实现(使用100%纯Java代码)。

注意,不要混淆javax.swing.filechooser.FileFilter抽象类与java.io.FileFilter接口。尽管功能类似,但是他们是不同的。他们两个共存是因为java.io.FileFilter接口并不存在于Java 1.1运行时中。因为原始的Swing JFileChooser需要同时运行在Java 1.1与Java 2一部 ,选择器需要定义一个替换。除非特别指定,本部分的所有FileFilter引用位于javax.swing.filechooser包中的类。

9.5.1 创建JFileChooser

JFileChooser有六个构造函数:

public JFileChooser()
JFileChooser fileChooser = new JFileChooser();

public JFileChooser(File currentDirectory)
File currentDirectory = new File("."); // starting directory of program
JFileChooser fileChooser = new JFileChooser(currentDirectory);

public JFileChooser(File currentDirectory, FileSystemView fileSystemView)
FileSystemView fileSystemView = new SomeFileSystemView(...);
JFileChooser fileChooser = new JFileChooser(currentDirectory, fileSystemView);

public JFileChooser(FileSystemView fileSystemView)
JFileChooser fileChooser = new JFileChooser(fileSystemView);

public JFileChooser(String currentDirectoryPath)
String currentDirectoryPath = "."; // starting directory of program
JFileChooser fileChooser = new JFileChooser(currentDirectoryPath);

public JFileChooser(String currentDirectoryPath, FileSystemView fileSystemView)
JFileChooser fileChooser = new JFileChooser(currentDirectoryPath, fileSystemView);


默认情况下,显示的起始目录是用户主目录(系统属性user.home)。如果我们希望启动JFileChooser指向其他的目录,这个目录可以使用String或是File对象进行指定。

我们也可以指定一个FileSystemView来指定操作系统顶层目录结构的自定义表示。当没有指定FileSystemView参数时,JFileChooser使用适合于用户操作系统的FileSystemView。

9.5.2 使用JFileChooser

在由构造函数创建JFileChooser之后,我们可以将其放在任何的Container中,因为他是一个JComponent。位于非弹出窗口对象中的JFileChooser对象看起来有一些奇怪,但是这可以使得我们完成一些无需创建新文件选择器的任务。

列表9-13演示了带有两个标签以及一个JFileChooser的简单窗体。注意,这个窗体并没有Open或是Cancel按钮,但是位于FileSystemView区域的按钮是可选择的。

package swingstudy.ch09;

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.Font;

import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;

public class FileSamplePanel {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub

		Runnable runner = new Runnable() {
			public void run() {
				JFrame frame = new JFrame("JFileChooser Popup");
				frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

				final JLabel directoryLabel = new JLabel("");
				directoryLabel.setFont(new Font("Serif", Font.BOLD | Font.ITALIC, 36));
				frame.add(directoryLabel, BorderLayout.NORTH);

				final JLabel filenameLabel = new JLabel("");
				filenameLabel.setFont(new Font("Serif", Font.BOLD | Font.ITALIC, 36));
				frame.add(filenameLabel, BorderLayout.SOUTH);

				JFileChooser fileChooser = new JFileChooser(".");
				fileChooser.setControlButtonsAreShown(false);
				frame.add(fileChooser, BorderLayout.CENTER);

				frame.pack();
				frame.setVisible(true);

			}
		};

		EventQueue.invokeLater(runner);
	}

}


向JFileChooser添加ActionListener

JFileChooser允许我们添加ActionListener对象来监听确认或是关闭动作的选择。确认是双击一个文件;关闭是按下Escape按键。要检测激发了哪一个动作,我们可以检测我们的ActionLister所接收到的ActionEvent的动作命令。其动作命令设置可以为用于文件选择的JFileChooser.APPROVE_SELECTION或是用于按下Escape按键的JFileChooser.CANCEL_SELECTION。

为了完成前面列表9-13中的示例,添加一个ActionListener从而使得我们在用户选择文件时设置两个标签的文本。一旦选择,文本变为当前目录与文件名。一旦按下Escape按键,文本会被清除。列表9-14显示了新的ActionListener。

// create ActionListener
				ActionListener actionListener = new ActionListener() {
					public void actionPerformed(ActionEvent event) {
						JFileChooser theFileChooser = (JFileChooser)event.getSource();
						String command = event.getActionCommand();
						if(command.equals(JFileChooser.APPROVE_SELECTION)) {
							File selectedFile = theFileChooser.getSelectedFile();
							directoryLabel.setText(selectedFile.getParent());
							filenameLabel.setText(selectedFile.getName());
						}
						else if(command.equals(JFileChooser.CANCEL_SELECTION)) {
							directoryLabel.setText("");
							filenameLabel.setText("");
						}
					}
				};

				fileChooser.addActionListener(actionListener);


通过使用这个ActionListener,由选择被激活的角度来说程序是完整的了。图9-26显示了在选中了C:\jdb1.5.0目录下的COPYRIGHT文件之后窗口的样子。


在弹出窗口中显示JFileChooser

除了可以将JFileChooser放在我们自己的窗口以外,我们更为通常的是将其放在一个模态JDialog中。依据我们希望在确认按钮上所显示的文本,有三种方法可以实现:

  • public int showDialog(Component parentComponent, String approvalButtonText)
  • public int showOpenDialog(Component parentComponent)
  • public int showSaveDialog(Component parentComponent)

调用这些方法中的任何一个都可以将配置的JFileChooser放在一个模态JDialog中,并且在父组件的中间位置显示对话框。提供一个null父组件参数会将弹出窗口放在屏幕中间。这个方法只有当用户选择确认或是关闭按钮时才会返回。在选择这两个按钮中的一个之后,调用会依据哪一个按钮被选中而返回一个状态值。这个状态值可以是JFileChooser的三个常量之一:APPROVE_OPTION, CANCEL_OPTION或是ERROR_OPTION。

注意,如果用户点击了确认按钮而没有选择任何文件,则CANCEL_OPTION会返回。

为了执行与前面的例子相同的任务,其中是将一个ActionListener关联到JFileChooser,我们可以显示这个对话框并依据返回状态修改标签,而是不依赖于动作命令,如下所示:

JFileChooser fileChooser = new JFileChooser(".");
int status = fileChooser.showOpenDialog(null);
if (status == JFileChooser.APPROVE_OPTION) {
  File selectedFile = fileChooser.getSelectedFile();
  directoryLabel.setText(selectedFile.getParent());
  filenameLabel.setText(selectedFile.getName());
}  else if (status == JFileChooser.CANCEL_OPTION) {
  directoryLabel.setText(" ");
  filenameLabel.setText(" ");
}


使用这一技术,文件选择器将会在另一个窗口中显示,而不是在具有两个标签的窗口中显示。注意,这个版本是由检测前面示例中的String返回值切换到检测int返回值:[if (command.equals(JFileChooser.APPROVE_SELECTION)) versus if (status == JFileChooser.APPROVE_OPTION)].

9.5.3 JFileChooser属性

一旦我们理解了基本的JFileChooser的使用,我们可以通过修改其属性来自定义组件的行为与外观。表9-10显示了JFileChooser的26个属性。



当使用不同的showDialog()方法时,dialogType属性被自动设置为JOptionPane的三个常量之一:OPEN_DIALOG, SAVE_DIALOG, CUSTOM_DIALOG。如果我们没有使用showDialog(),我们应该依据我们计划使用的对话框类型来设置这个属性。controlButtonsAreShown属性可以使得我们隐藏Open, Save与Cancel按钮。

9.5.4 使用文件过滤器

JFileChooser支持三种过滤其文件与目录列表的方法。前两个涉及到使用FileFilter类,而最后一个涉及到隐藏文件。首先,我们来看一下FileFilter类。

FileFilter是一个抽象类,其工作方式类似于AWT中的FilenameFilter。然而,这个并不使用目录或是文件名的字符串,而是使用File对象。对于每一个要显示的File对象(文件与目录),过滤器决定File是否要显示在JFileChooser中。除了提供一个接受机制,当向用户显示描述时,过滤器同时提供了描述或名字。在下面的类定义的两个方法反映了这种功能:

public abstract class FileFilter {
  public FileFilter();
  public abstract String getDescription();
  public abstract boolean accept(File file);
}


注意,由于这个类的抽象特性,他本应是一个接口,但事实上不是。

为了演示文件过滤器,列表9-15创建了一个可以接受一个文件扩展名数组的过滤器。如果发送给accept()方法的文件是一个目录,则会被自动接受。否则,文件扩展名必须与所提供的数组中的扩展名匹配,而且扩展名前的字符必须是一个句点。对于这个特定的过滤器,比较是大小写敏感的。

package swingstudy.ch09;

import java.io.File;

import javax.swing.filechooser.FileFilter;

public class ExtensionFileFilter extends FileFilter {

	String description;
	String extensions[];

	public ExtensionFileFilter(String description, String extension) {
		this(description, new String[] {extension} );
	}

	public ExtensionFileFilter(String description, String extensions[]) {
		if(description == null) {
			// since no description, use first extension and # of extensions as description
			this.description = extensions[0]+"{ "+extensions.length+"} ";
		}
		else {
			this.description = description;
		}

		// convert array to lowercase
		// don't alter original entries
		this.extensions = (String[])extensions.clone();
		toLower(this.extensions);
	}

	private void toLower(String array[]) {
		for(int i=0, n=array.length; i<n; i++) {
			array[i] = array[i].toLowerCase();
		}
	}

	// ignore case, always accept directories
	// character before extension must be a period
	@Override
	public boolean accept(File file) {
		// TODO Auto-generated method stub
		if(file.isDirectory()) {
			return true;
		}
		else {
			String path = file.getAbsolutePath().toLowerCase();
			for(int i=0, n=extensions.length; i<n; i++) {
				String extension = extensions[i];
				if(path.endsWith(extension) && (path.charAt(path.length()-extension.length()-1)=='.')) {
					return true;
				}
			}
		}
		return false;
	}

	@Override
	public String getDescription() {
		// TODO Auto-generated method stub
		return description;
	}

}


使用这个文件过滤器可以使得我们创建并将其关联到JFileChooser。如果我们只是想使得过滤器可以为用户选择,但是并不是默认的初始选择,可以调用public void addChoosableFileFilter(FileFilter filter)。这可以使得默认的接受所有文件的过滤器被选中。相反,如果我们希望过滤参选择器第一次出现时设置,调用public void setFileFilter(FileFilter filter)方法,而文件选择器将会过滤所显示的初始文件集合。

例如,下面的源码将会向文件选择器添加两个过滤器:

FileFilter jpegFilter =
  new ExtensionFileFilter(null, new String[]{ "JPG", "JPEG"} );
fileChooser.addChoosableFileFilter(jpegFilter);
FileFilter gifFilter = new ExtensionFileFilter("gif", new String[]{ "gif"} );
fileChooser.addChoosableFileFilter(gifFilter);

当没有文件过滤器与JFileChooser相关联时,JFileChooser.getAcceptAllFileFilter()中的过滤器会被用来提供一个接受所有文件的过滤器,而这也同样适用于底层操作系统。

图9-27显示了Motif文件选择器中的一个打开的过滤器选择组合框。


提示,在使用addChoosableFileFilter()方法添加过滤器之前使用setFileFilter()方法设置FileFilter会使得接受所有文件的过滤器不可用。要恢复这个过滤器,调用setAcceptAllFileFilterUsed(true)方法。另外,我们可以使用resetChoosableFileFilters()方法重新设置过滤器列表。

内建的过滤器并不是FileFilter。他关注于隐藏文件,例如Unix文件系统上以句点(.)开始的文件。默认情况下,隐藏文件并不会显示在JFileChooser中。要允许显示隐藏文件,我们必须将fileHidingEnabled属性设置为false:

aFileChooser.setFileHidingEnabled(false);

提示,当创建javax.swing.filechooser.FileFilter子类时,我们也许会希望新类同时实现java.io.FileFilter接口。要实现这一目的,只需要简单的将implements java.io.FileFilter添加到类定义。这样做是因为javax.swing.filechooser类中的accept()方法的方法签名与接口定义相匹配:public boolean accept(File file)。

选择目录而不选择文件

JFileChooser支持三种选择模式:只选择文件,只选择目录,同时选择文件与目录。fileSelectionMode属性设置决定了选择器的模式。可用的设置是通过三个JFileChooser常量来指定的:FILES_ONLY, DIRECTORIES_ONLY以及FILES_AND_DIRECTORIES。初始时,文件选择器位于JFileChooser.FILES_ONLY模式。要修改模式,只需要调用public void setFileSelectionMode(int newMode)。

除了fileSelectionMode属性,我们可以使用只读的fileSelectionEnabled与directorySelectionEnabled属性来决定文件选择当前所支持的输入类型。

添加附加面板

JFileChooser支持附加组件的添加。这个组件可以加强选择器的功能,包括预览图片或文档,或是播放音频文件。要响应文件选择变化,附加组件应将其自己作为PropertyChangeListener关联到JFileChooser。当JFileChooser.SELECTED_FILE_CHANGED_PROPERTY属性变化时,附加组件发生变化来反映文件选择。图9-28显示了一个图片预览附加组件的样子。配置选择器的附加性就如同设置其他属性一样。

fileChooser.setAccessory(new LabelAccessory(fileChooser));


列表9-16显示了显示一个附加图标的Image组件的源码。被选中的图像文件变为JLabel组件的图标。组件执行两个缩放操作来保证图像的维度适合附加组件的尺寸。

package swingstudy.ch09;

import java.awt.Dimension;
import java.awt.Image;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;

import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
import javax.swing.JLabel;

public class LabelAccessory extends JLabel implements PropertyChangeListener {

	private static final int PREFERRED_WIDTH = 125;
	private static final int PREFERRED_HEIGHT = 100;

	public LabelAccessory(JFileChooser chooser) {
		setVerticalAlignment(JLabel.CENTER);
		setHorizontalAlignment(JLabel.CENTER);
		chooser.addPropertyChangeListener(this);
		setPreferredSize(new Dimension(PREFERRED_WIDTH, PREFERRED_HEIGHT));
	}

	@Override
	public void propertyChange(PropertyChangeEvent event) {
		String changeName = event.getPropertyName();
		if(changeName.equals(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)){
			File file = (File)event.getNewValue();
			if(file != null) {
				ImageIcon icon = new ImageIcon(file.getPath());
				if(icon.getIconWidth() > PREFERRED_WIDTH) {
					icon = new ImageIcon(icon.getImage().getScaledInstance(PREFERRED_WIDTH, -1, Image.SCALE_DEFAULT));
					if(icon.getIconHeight() > PREFERRED_HEIGHT) {
						icon = new ImageIcon(icon.getImage().getScaledInstance(-1, PREFERRED_HEIGHT, Image.SCALE_DEFAULT));
					}
				}
				setIcon(icon);
			}
		}
	}
}


使用FileSystemView类

FileSystemView类可以访问平台相关的文件系统信息。java.io.File的JDK 1.1版本在这方面比较弱,FileSystemView的出现使得设计FileChooserUI对象更为容易。Swing的FileSystemView类以FileSystemView的包私有子类的方式提供了三个自定义的视图。他们包括对Unix,Windows以及一个通用处理器的支持。

尽管并没有必要来定义我们自己的FileSystemView,这个类提供了一些在JFileChooser环境之外十分有用的特性。要获取特定于用户运行时环境的视图,可以调用public static FileSystemView getFileSystemView()方法。这个类的定义如下:

public abstract class FileSystemView {
  // Constructors
  public FileSystemView();  // Properties
  // Properties
  public File getDefaultDirectory();
  public File getHomeDirectory();
  public File[] getRoots();
  // Class Methods
  public static FileSystemView getFileSystemView();
  // Other Methods
  public File createFileObject(File directory, String filename);
  public File createFileObject(String path);
  protected File createFileSystemRoot(File file);
  public abstract File createNewFolder(File containingDir) throws IOException;
  public File getChild(File parent, String filename);
  public File[] getFiles(File directory, boolean useFileHiding);
  public File getParentDirectory(File file);
  public String getSystemDisplayName(File file);
  public Icon getSystemIcon(File file);
  public String getSystemTypeDescription(File file);
  public boolean isComputerNode(File file);
  public boolean isDrive(File file);
  public boolean isFileSystem(File file);
  public boolean isFileSystemRoot(File file);
  public boolean isFloppyDrive(File file);
  public boolean isHiddenFile(File file);
  public boolean isParent(File folder, File file);
  public boolean isRoot(File file);
  public Boolean isTraversable(File file);
}


注意,isTraversable()方法返回Boolean,而不是boolean。

FileView类

我们将要探讨的JFileChooser的最后一部分就是FileView区域,其中列出了所有的文件名。每一个自定义的观感类型都有其自己的FileView区域类。另外,某些预定义的观感类型,例如Motif,是不可改变的。然而,至少在Metal与Windows文件选择器,我们可以为不同的文件类型自定义图标,或是修改文件的显示名字。

FileView类的五个方法允许我们修改视图中每一个File的名字,图标或是描述(两种形式)。另外,FileView实际上控制一个目录是否是可遍历的,从而使得我们可以以访问控制的弱级别进行编程。不可遍历的目录具有一个不同的默认图标,因为这些目录不能用于文件选择的浏览。

下面是抽象的FileView类的定义:

public abstract class FileView {
  public FileView();
  public String getDescription(File file);
  public Icon getIcon(File file);
  public String getName(File file);
  public String getTypeDescription(File file);
  public Boolean isTraversable(File file);
}


注意,类似FileSystemView,isTraversable()方法返回Boolean,而不是boolean。

自定义FileView需要创建一个子类并重写相应的方法。默认情况下,所有的方法返回null,表明我们并不希望为特定的方法定制自定义行为。

一旦我们定义了五个视图,简单的修改我们JFileChooser的fileView属性:

fileChooser.setFileView(new JavaFileView());

图9-29显示了一个Metal JFileChooser在安装了自定义的FileView之后的外观样式。


列表9-17中的JavaFileView类提供了一个FileView实现,这个实现自定义了与Java开发相关的文件的显示,特别是.java, .class, .jar以及.html或是.htm文件。对于这些文件类型中的每一种,一个特殊的图标替换了默认图标显示在文件名旁边。另外,对于Java源文件,显示文件长度。不幸的是,我们不可以修改FileView中的字体或颜色。

package swingstudy.ch09;

import java.awt.Color;
import java.io.File;

import javax.swing.Icon;
import javax.swing.filechooser.FileView;

import swingstudy.ch04.DiamondIcon;

public class JavaFileView extends FileView {

	Icon javaIcon =new DiamondIcon(Color.BLUE);
	Icon classIcon = new DiamondIcon(Color.GREEN);
	Icon htmlIcon = new DiamondIcon(Color.RED);
	Icon jarIcon = new DiamondIcon(Color.PINK);

	public String getName(File file) {
		String filename = file.getName();
		if(filename.endsWith(".java")) {
			String name = filename +" : "+file.length();
			return name;
		}
		return null;
	}

	public String getTypeDescription(File file) {
		String typeDescription = null;
		String filename = file.getName().toLowerCase();

		if(filename.endsWith(".java")) {
			typeDescription = "Java Source";
		}
		else if(filename.endsWith(".class")) {
			typeDescription = "Java Class File";
		}
		else if(filename.endsWith(".jar")) {
			typeDescription = "Java Archive";
		}
		else if(filename.endsWith(".html") || filename.endsWith(".htm")) {
			typeDescription = "Applet Loader";
		}

		return typeDescription;
	}

	public Icon getIcon(File file) {
		if(file.isDirectory()) {
			return null;
		}
		Icon icon = null;
		String filename = file.getName().toLowerCase();
		if(filename.endsWith(".java")) {
			icon = javaIcon;
		}
		else if(filename.endsWith(".class")) {
			icon = classIcon;
		}
		else if(filename.endsWith(".jar")) {
			icon = jarIcon;
		}
		else if(filename.endsWith(".html") || filename.endsWith(".htm")) {
			icon = htmlIcon;
		}

		return icon;
	}
}


9.5.5 自定义JFileChooser观感

每一个可安装的Swing观感提供了不同的JFileChooser外观以及默认的UIResource值集合。图9-30显示了预安装的观感集合,Motif,Windows,以及Ocean的JFileChooser外观。




JFileChooser可用的UIResource相关属性集合显示在表9-11中。对于JFileChooser组件,有83个不同的属性。几乎所有的属性都与按钮标签,热键,图标与工具提示文本相关。







除了80多个的JFileChooser资源以外,另外还有FileView的五个,显示在表9-12中。


9.6 小结

在本章中,我们探讨了Swing弹出窗口以及选择器类的细节。除了后动创建一个JDialog并且为其填充必要的部分外,Swing组件集合包含了对多个不同的弹出窗口与选择器类的支持。由JOptionPane开始,我们了解了如何创建信息,问题,以及输入弹出窗口。另外,我们探讨了如何通过使用ProgressMonitor与ProgressMonitorInputStream类来监视需要长时间完成的任务的进程。

在了解了通用的弹出类之后,我们探讨了特殊的Swing颜色以及文件选择器类:JColorChooser与JFileChooser。通过这两个类,我们可以提示用户用于请求输入以及以我们可以想像的更多的方式自定义显示。

现在我们已经对预定义的弹出窗口有一定的了解了,现在是开始第10章LayoutManager类的讨论的时候了。借助于系统布局管理器,我们可以创建更好的用户界面。


分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics