J2ME(JSR75)组件之文件选择器
????? 在J2ME开发的时候,可能会需要浏览手机的文件目录,但是又没有和J2SE里面的JFileChooser一样的组件可以用,只有自己写一个了,在写的过程中,发现了一些问题,在此与大家分享一下。
一开始我以为,只要是支持JSR75的手机都可以支持手机内所有文件的访问,可是在真机上一看才知道,手机的文件或者文件夹有公有与有私有之分,我们看上去像是公有的文件夹,在J2ME里面却不能访问。比如我测试用的手机是诺基亚的N76,它的SD卡上的Music目录,对于程序来说,就是私有的,不能访问的,而"手机动漫"这个目录却是能访问的。难怪我测了很多次放在Music目录里面的歌曲,怎么播也播不出来,后来经过一步一步的调试,才知道原来此目录下面的文件不可读。要知道ME的调试是多么不方便,又不能用System.out.println调试,因为在真机上面根本就没有输出窗口。只能自己一句一句用Alert来调试。
不说废话了,先给出代码吧。这是一个继承自 List的组件。用列表的方式显示出当前目录下的所有文件。本来是想全新写一个的,后来发现netbeans有一个,所以就直接用了它的,写得很不错,说到这里,我觉得netbeans很多地方确实不错,只是很多人由于以前的偏见没有给它机会而已。
/* * To change this template, choose Tools | Templates * and open the template in the editor. */import java.io.IOException;import java.util.Enumeration;import javax.microedition.io.Connector;import javax.microedition.io.file.FileConnection;import javax.microedition.io.file.FileSystemRegistry;import javax.microedition.lcdui.Alert;import javax.microedition.lcdui.AlertType;import javax.microedition.lcdui.Command;import javax.microedition.lcdui.CommandListener;import javax.microedition.lcdui.Display;import javax.microedition.lcdui.Displayable;import javax.microedition.lcdui.Image;import javax.microedition.lcdui.List;/** * * * * * @author hadeslee */public class FileBrowser extends List implements CommandListener {/** * * Command fired on file selection. */public static final Command SELECT_FILE_COMMAND = new Command("选择",Command.OK, 1);private String currDirName;private String currFile;private Image dirIcon;private Image fileIcon;private CommandListener commandListener;/* special string denotes upper directory */private static final String UP_DIRECTORY = "..";/* * special string that denotes upper directory accessible by this browser. * * this virtual directory contains all roots. */private static final String MEGA_ROOT = "/";/* separator string as defined by FC specification */private static final String SEP_STR = "/";/* separator character as defined by FC specification */private static final char SEP = '/';private Display display;private String selectedURL;private String filter = null;private String title;/** * * Creates a new instance of FileBrowser for given <code>Display</code> * object. * * * @param display * non null display object. */public FileBrowser(Display display) {super("文件浏览器", IMPLICIT);currDirName = MEGA_ROOT;this.display = display;super.setCommandListener(this);setSelectCommand(SELECT_FILE_COMMAND);try {dirIcon = Image.createImage(this.getClass().getResourceAsStream("dir.png"));} catch (IOException e) {dirIcon = null;}try {fileIcon = Image.createImage(this.getClass().getResourceAsStream("file.png"));} catch (IOException e) {fileIcon = null;}showDir();}/** * * 显示当前的文件夹 */private void showDir() {new Thread(new Runnable() {public void run() {try {showCurrDir();} catch (SecurityException e) {Alert alert = new Alert("错误", "您没有权限访问此文件或文件夹!", null,AlertType.ERROR);alert.setTimeout(2000);display.setCurrent(alert, FileBrowser.this);} catch (Exception e) {e.printStackTrace();}}}).start();}/** * * Indicates that a command event has occurred on Displayable d. * * * @param c * a <code>Command</code> object identifying the command. This * is either * one of the applications have been added to * <code>Displayable</code> with * <code>addCommand(Command)</code> * or is the implicit * <code>SELECT_COMMAND</code> of List. * * @param d * the <code>Displayable</code> on which this event has * occurred */public void commandAction(Command c, Displayable d) {if (c.equals(SELECT_FILE_COMMAND)) {List curr = (List) d;currFile = curr.getString(curr.getSelectedIndex());new Thread(new Runnable() {public void run() {if (currFile.endsWith(SEP_STR)|| currFile.equals(UP_DIRECTORY)) {openDir(currFile);} else {// switch To NextdoDismiss();}}}).start();} else {if (commandListener != null) {commandListener.commandAction(c, d);}}}/** * * Sets component's title. * * * @param title * component's title. */public void setTitle(String title) {this.title = title;super.setTitle(title);}/** * * Show file list in the current directory . */private void showCurrDir() {if (title == null) {super.setTitle(currDirName);}Enumeration e = null;FileConnection currDir = null;deleteAll();if (MEGA_ROOT.equals(currDirName)) {append(UP_DIRECTORY, dirIcon);e = FileSystemRegistry.listRoots();} else {try {currDir = (FileConnection) Connector.open("file:///"+ currDirName);e = currDir.list();} catch (IOException ioe) {}append(UP_DIRECTORY, dirIcon);}if (e == null) {try {currDir.close();} catch (IOException ioe) {ioe.printStackTrace();}return;}while (e.hasMoreElements()) {String fileName = (String) e.nextElement();if (fileName.charAt(fileName.length() - 1) == SEP) {// This is directoryappend(fileName, dirIcon);} else {// this is regular fileif (filter == null || fileName.indexOf(filter) > -1) {append(fileName, fileIcon);}}}if (currDir != null) {try {currDir.close();} catch (IOException ioe) {ioe.printStackTrace();}}}private void openDir(String fileName) {/* * In case of directory just change the current directory * and show it */if (currDirName.equals(MEGA_ROOT)) {if (fileName.equals(UP_DIRECTORY)) {// can not go up from MEGA_ROOTreturn;}currDirName = fileName;} else if (fileName.equals(UP_DIRECTORY)) {// Go up one directory// TODO use setFileConnection when implementedint i = currDirName.lastIndexOf(SEP, currDirName.length() - 2);if (i != -1) {currDirName = currDirName.substring(0, i + 1);} else {currDirName = MEGA_ROOT;}} else {currDirName = currDirName + fileName;}showDir();}/** * * Returns selected file as a <code>FileConnection</code> object. * * * @return non null <code>FileConection</code> object */public FileConnection getSelectedFile() throws IOException {FileConnection fileConnection = (FileConnection) Connector.open(selectedURL);return fileConnection;}/** * * Returns selected <code>FileURL</code> object. * * * @return non null <code>FileURL</code> object */public String getSelectedFileURL() {return selectedURL;}/** * * Sets the file filter. * * * @param filter * file filter String object */public void setFilter(String filter) {this.filter = filter;}/** * * Returns command listener. * * * @return non null <code>CommandListener</code> object */protected CommandListener getCommandListener() {return commandListener;}/** * * Sets command listener to this component. * * * @param commandListener * <code>CommandListener</code> to be used */public void setCommandListener(CommandListener commandListener) {this.commandListener = commandListener;}private void doDismiss() {selectedURL = "file:///" + currDirName + currFile;CommandListener listener = getCommandListener();if (listener != null) {listener.commandAction(SELECT_FILE_COMMAND, this);}}}?
这个类可以用做浏览手机文件之用,也可以用做得到得选的文件之用,如果想要在选择了文件以后做什么事情的话,可以调用它的setCommandListener方法。并且处理SELECT_FILE_COMMAND.
经过试验,我发现所有的只读文件夹对于FileConnection来说,就是私有的,是不能访问到的,所以当我们要访问的时候,最好是把那些只读的文件夹改为可读写。
发现一件N76的文件的好玩的事情。在N76里面,你访问的时候,总共有四个根文件夹,分别是:手机存储/??? C:/??? 存储卡/??? E:/可是我发现手机存储和C是一样的,存储卡和E也是一样的,也就是说可以用file:///手机存储/来访问也可以用file:///C:/来访问。