因此想實作簡單的檔案總管的樹狀目錄時,可以透過 Tree 元件來實作。
首先,第一步要先產生一個 Tree,並且設定 Tree 這個容器的尺寸
Tree dirTree = new Tree(parent, SWT.BORDER | SWT.MULTI | SWT.V_SCROLL); dirTree.setSize(width, height);
接著,因為要做樹狀目錄,因此程式剛開啟時,要顯示出系統的根目錄們。可以利用 File 的相關方法來取得系統的根目錄。
File[] roots = File.listRoots();
想要在 Tree 上面加入子項目時,必須初始化一個 TreeItem,並且透過建構子 TreeItem(Tree parent, int style) 讓產生的 TreeItem 變成 Tree 的子項目。
TreeItem dirItem = dirItem = new TreeItem(dirTree, 0); dirItem.setText(data.getAbsolutePath());
而因為預設 TreeItem 是沒有子項目的,因此不會有展開的選項可以讓使用者點選。
為了要有一個展開的選項(即使程式實際上還沒讀到那層子目錄),要預先在 TreeItem 裡放一個假的子項目
因此上述的初始化 TreeItem 的程式碼就會變成這樣:
TreeItem dirItem = dirItem = new TreeItem(dirTree, 0); dirItem.setText(data.getAbsolutePath()); dirItem.setData(TREE_DATA_FILE, data); TreeItem fakeChild = new TreeItem(dirItem, 0);
第三行是把 TreeItem 所代表的 Path 存在 TreeItem 的 Data 欄位上,方便日後有事件觸發時,可以隨時得知觸發的 TreeItem 表示的是哪層路徑。
第四行就是產生一個假的 TreeItem 作為子項目。
有了 TreeItem 之後,要設定當 Tree 被展開時,要動態地讀取 TreeItem 所屬的路徑當中的所有目錄,因此要對 Tree 加上 Listener,如下所示。
TreeItem item = (TreeItem) event.item; File path = (File) item.getData(TREE_DATA_FILE); // Remove the fake (or the existing) children. item.removeAll(); // Traverse the path and get all of the sub directories. File[] files = path.listFiles(); for(File file : files) { if(file.isDirectory()) { addChildToDirectoryTree(item, file); } }
其中 addChildToDirectoryTree() 這個函式是把上一個步驟中,產生子項目以及設定初始狀態的程式碼集中起來寫成一個函式。
基本來說,目前為止就已經可以產生一個能夠展開的樹狀目錄了。
在額外加上一些簡單的 error handling 之後,組合起來就變成以下的程式碼了。
import java.io.File; import java.nio.file.Files; import org.apache.log4j.Logger; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeItem; import org.eclipse.swt.widgets.Widget; public class DirectoryTree { private static Logger log = Logger.getLogger("Component.DTree"); private static String TREE_DATA_FILE = "FILE"; public static void createDirectoryTree (Composite parent, int width, int height) { Tree dirTree = new Tree(parent, SWT.BORDER | SWT.MULTI | SWT.V_SCROLL); dirTree.setSize(width, height); dirTree.addListener(SWT.Expand, new Listener() { @Override public void handleEvent(Event event) { log.debug("Expand event is triggered."); TreeItem item = (TreeItem) event.item; File path = (File) item.getData(TREE_DATA_FILE); log.debug("Get the path " + path.getAbsolutePath()); // Remove the fake (or the existing) children. item.removeAll(); // Traverse the path and get all of the sub directories. File[] files = path.listFiles(); log.debug("List " + files.length); for(File file : files) { if(file.isDirectory() && Files.isReadable(file.toPath()) // From Java SE 7's API && !Files.isSymbolicLink(file.toPath())) { // From Java SE 7's API addChildToDirectoryTree(item, file); } } } }); // Get the root directories. File[] roots = File.listRoots(); for(File root : roots) { addChildToDirectoryTree(dirTree, root); } } private static void addChildToDirectoryTree (Widget parent, File data) { TreeItem dirItem = null; if (parent instanceof Tree) { dirItem = new TreeItem((Tree) parent, 0); dirItem.setText(data.getAbsolutePath()); } else { dirItem = new TreeItem((TreeItem) parent, 0); dirItem.setText(data.getName()); } dirItem.setData(TREE_DATA_FILE, data); TreeItem fakeChild = new TreeItem(dirItem, 0); log.debug("Add directory " + ((File)dirItem.getData(TREE_DATA_FILE)).getAbsolutePath()); } }
比較特別的地方是 36、37 行,在增加子項目時,除了檢查 File 物件是不是表示一個資料夾之外,又額外檢查了兩個奇怪的條件
這個部分可以參考 [2] 的討論,實際上如果不加這個檢查的話,大部分的目錄操作都沒有問題
但遇到像是 C:\Document and Settings 時,程式就會意外地拋出 NullPointerException
認真去追蹤的話,結果會發現 Exception 的來源會是 32 行的 File[] files = path.listFiles();
原因在於 C:\Document and Settings 這個目錄,在 Vista 以後的 Windows 作業系統上,是一個實際上並不存在的資料夾
(也就是一個 Symbolic Link)
同時這個目錄並不具備足夠的讀取權限,因為 JVM 無法讀取內容,也代表著無從得知這個目錄是不是一個 Symbolic Link
(因為無權限讀取,所以系統也不會真的告訴你它是什麼東西)
所以必須把這些無法讀取內容的路徑去除掉,才能避免使用者點一點就把程式點壞了 XD
執行結果如下圖,當然在執行前要自己產生 main(),還有要呼叫 createDirectoryTree() 之前要先做出 Display 和 Shell
這些如果不會的話,都可以在網路上輕易找到其他的入門教學或範例程式。
參考資料:
1、17.60.5.Tree Exapand listener
2、Java finds a “Documents and Settings” folder on Win 7 HP. I can't see one
沒有留言:
張貼留言