JAVAME(JSR75)组件之文件选择器

    在JAVAME开发的时候,可能会需要浏览手机的文件目录,但是又没有和JAVASE里面的JFileChooser一样的组件可以用,只有自己写一个了,在写的过程中,发现了一些问题,在此与大家分享一下.
   
一开始我以为,只要是支持JSR75的手机都可以支持手机内所有文件的访问,可是在真机上一看才知道,手机的文件或者文件夹有公有与有私有之分,我们看上
去像是公有的文件夹,在JAVAME里面却不能访问.比如我测试用的手机是诺基亚的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.
 */
package com.hadeslee.test;

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 Display 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 Command object identifying the command. This is either
    * one of the applications have been added to Displayable with addCommand(Command)
    * or is the implicit SELECT_COMMAND of List.
    * @param d the Displayable 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 Next
                  doDismiss();
               }
            }
         }).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 directory
            append(fileName, dirIcon);
         }
         else {
            // this is regular file
            if (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_ROOT
            return;
         }
         currDirName = fileName;
      }
      else if (fileName.equals(UP_DIRECTORY)) {
         // Go up one directory
         // TODO use setFileConnection when implemented
         int 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 FileConnection object.
    * @return non null FileConection object
    */
   public FileConnection getSelectedFile() throws IOException {
      FileConnection fileConnection = (FileConnection) Connector.open(
         selectedURL);
      return fileConnection;
   }

   /**
    * Returns selected FileURL object.
    * @return non null FileURL 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 CommandListener object
    */
   protected CommandListener getCommandListener() {
      return commandListener;
   }

   /**
    * Sets command listener to this component.
    * @param commandListener CommandListener 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:/来访问.