++ 樹(Tree)的使用與介紹
10-1:使用JTree組件:
java.lang.Object
--java.awt.Component
--java.awt.Container
--javax.swing.JComponent
--javax.swing.JTree
JTree構造函數:
JTree():建立一棵系統預設的樹。
JTree(Hashtable value):利用Hashtable建立樹,不顯示root node(根節點).
JTree(Object[] value):利用Object Array建立樹,不顯示root node.
JTree(TreeModel newModel):利用TreeModel建立樹。
JTree(TreeNode root):利用TreeNode建立樹。
JTree(TreeNode root,boolean asksAllowsChildren):利用TreeNode建立樹,並決
定是否允許子節點的存在.
JTree(Vector value):利用Vector建立樹,不顯示root node.
範例:
InitalTree.java
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class InitalTree{
public InitalTree(){
JFrame f=new JFrame("TreeDemo");
Container contentPane=f.getContentPane();
JTree tree=new JTree();
JScrollPane scrollPane=new JScrollPane();
scrollPane.setViewportView(tree);
contentPane.add(scrollPane);
f.pack();
f.setVisible(true);
f.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e){
System.exit(0);
}
});
}
public static void main(String[] args){
new InitalTree();
}
}
10-2:以Hashtable構造JTree:
上面的例子對我們並沒有裨的幫助,因為各個節點的數據均是java的預設值,
而非我們自己設置的。因此我們需利用其他JTree
構造函數來輸入我們想要的節點數據。以下範例我們以Hashtable當作JTree的數據
輸入:
範例:TreeDemo1.java
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
public class TreeDemo1{
public TreeDemo1(){
JFrame f=new JFrame("TreeDemo1");
Container contentPane=f.getContentPane();
String[] s1={"公司文件","個人信件","私人文件"};
String[] s2={"本機磁盤(C:)","本機磁盤(D:)","本機磁盤(E:)"};
String[] s3={"奇摩站","職棒消息","網絡書店"};
Hashtable hashtable1=new Hashtable();
Hashtable hashtable2=new Hashtable();
hashtable1.put("我的公文包",s1);
hashtable1.put("我的電腦",s2);
hashtable1.put("收藏夾",hashtable2);
hashtable2.put("網站列表",s3);
Font font = new Font("Dialog", Font.PLAIN, 12);
Enumeration keys = UIManager.getLookAndFeelDefaults().keys();
/**定義widnows界面**/
while (keys.hasMoreElements()) {
Object key = keys.nextElement();
if (UIManager.get(key) instanceof Font) {
UIManager.put(key, font);
}
}
try{
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
}catch(Exception el){
System.exit(0);
}
/**定義widnows界面**/
JTree tree=new JTree(hashtable1);
JScrollPane scrollPane=new JScrollPane();
scrollPane.setViewportView(tree);
contentPane.add(scrollPane);
f.pack();
f.setVisible(true);
f.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e){
System.exit(0);
}
});
}
public static void main(String[] args){
new TreeDemo1();
}
}
純XP界面的設置:
10-3:以TreeNode構造JTree:
JTree上的每一個節點就代表一個TreeNode對象,TreeNode本身是一個
Interface,裡面定義了7個有關節點的方法,例如判斷是否
為樹葉節點、有幾個子節點(getChildCount())、父節點為何(getparent())等等、
這些方法的定義你可以在javax.swing.tree的
package中找到,讀者可自行查閱java api文件。在實際的應用上,一般我們不會
直接實作此界面,而是採用java所提供的
DefaultMutableTreeMode類,此類是實作MutableTreeNode界面而來,並提供了其
他許多實用的方法。MutableTreeNode本身也是一
個Interface,且繼承了TreeNode界面此類主要是定義一些節點的處理方式,例如新
增節點(insert())、刪除節點(remove())、設置
節點(setUserObject())等。整個關係如下圖:
TreeNode----extends--->MutableTreeNode---implements---DefaultMutableTreeNode
接下來我們來看如何利DefaultMutableTreeNode來建立JTree,我們先來看
DefaultMutableTreeNode的構造函數:
DefaultMutableTreeNode構造函數:
DefaultMutableTreeNode():建立空的DefaultMutableTreeNode對象。
DefaultMutableTreeNode(Object userObject):建立DefaultMutableTreeNode對
象,節點為userObject對象。
DefaultMutableTreeNode(Object userObject,Boolean allowsChildren):建立
DefaultMutableTreeNode對象,節點為userObject對
象並決定此節點是否允許具有子節點。
以下為利用DefaultMutableTreeNode建立JTree的範例:TreeDemo2.java
此程式"資源管理器"為此棵樹的根節點.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.tree.*;
public class TreeDemo2{
public TreeDemo2(){
JFrame f=new JFrame("TreeDemo2");
Container contentPane=f.getContentPane();
DefaultMutableTreeNode root=new DefaultMutableTreeNode("資源管理器");
DefaultMutableTreeNode node1=new DefaultMutableTreeNode("我的公文包");
DefaultMutableTreeNode node2=new DefaultMutableTreeNode("我的電腦");
DefaultMutableTreeNode node3=new DefaultMutableTreeNode("收藏夾");
DefaultMutableTreeNode node4=new DefaultMutableTreeNode("Readme");
root.add(node1);
root.add(node2);
root.add(node3);
root.add(node4);
DefaultMutableTreeNode leafnode=new DefaultMutableTreeNode("公司文件");
node1.add(leafnode);
leafnode=new DefaultMutableTreeNode("私人文件");
node1.add(leafnode);
leafnode=new DefaultMutableTreeNode("個人信件");
leafnode=new DefaultMutableTreeNode("本機磁盤(C:)");
node2.add(leafnode);
leafnode=new DefaultMutableTreeNode("本機磁盤(D:)");
node2.add(leafnode);
leafnode=new DefaultMutableTreeNode("本機磁盤(E:)");
node2.add(leafnode);
DefaultMutableTreeNode node31=new DefaultMutableTreeNode("網站列表");
node3.add(node31);
leafnode=new DefaultMutableTreeNode("奇摩站");
node31.add(leafnode);
leafnode=new DefaultMutableTreeNode("職棒消息");
node31.add(leafnode);
leafnode=new DefaultMutableTreeNode("網絡書店");
node31.add(leafnode);
JTree tree=new JTree(root);
JScrollPane scrollPane=new JScrollPane();
scrollPane.setViewportView(tree);
contentPane.add(scrollPane);
f.pack();
f.setVisible(true);
f.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e){
System.exit(0);
}
});
}
public static void main(String[] args){
new TreeDemo2();
}
}
10-4:以TreeModel構造JTree.
除了以節點的觀念(TreeNode)建立樹之外,你可以用data model的模式建立
樹。樹的data model稱為TreeModel,用此模式的好處
是可以觸發相關的樹事件,來處理樹可能產生的一些變動。TreeModel是一個
interface,裡面定義了8種方法;如果你是一個喜歡自己
動手做的人,或是你想顯示的數據格式很複雜,你可以考慮直接實作TreeModel界
面中所定義的方法來構造出JTree.TreeModel界面
的方法如下所示:
TreeModel方法:
void addTreeModelListener(TreeModelListener l):增加一個
TreeModelListener來監控TreeModelEvent事件。
Object getChild(Object parent,int index):返回子節點。
int getChildCount(Object parent):返回子節點數量.
int getIndexOfChild(Object parent,Object child):返回子節點的索引值。
Object getRoot():返回根節點。
boolean isLeaf(Object node):判斷是否為樹葉節點。
void removeTreeModelListener(TreeModelListener l):刪除
TreeModelListener。
void valueForPathChanged(TreePath path,Object newValue):當用戶改變
Tree上的值時如何應對。
你可以實作出這8種方法,然後構造出自己想要的JTree,不過在大部份的情況下
我們通常不會這樣做,畢竟要實作出這8種方法不
是件很輕鬆的事,而且java本身也提供了一個預設模式,叫做DefaultTreeModel,
這個類已經實作了TreeModel界面,也另外提供許
多實用的方法。利用這個預設模式,我們便能很方便的構造出JTree出來了。下面
為DefaultTreeModel的構造函數與範例:
DefaultTreeModel構造函數:
DefaultTreeModel(TreeNode root):建立DefaultTreeModel對象,並定出根節點。
DefaultTreeModel(TreeNode root,Boolean asksAllowsChildren):建立具有根節
點的DefaultTreeModel對象,並決定此節點是否允
許具有子節點。
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.tree.*;
import com.incors.plaf.alloy.*;//組件的下載網址http://www.incors.com
/lookandfeel/
/*將alloy.jar放在c:/j2sdk1.4.0/jre/lib/ext/目錄下.
*/
public class TreeDemo3
{
public TreeDemo3()
{
JFrame f = new JFrame("TreeDemo");
Container contentPane = f.getContentPane();
DefaultMutableTreeNode root = new DefaultMutableTreeNode("資源管
理器");
DefaultMutableTreeNode node1 = new DefaultMutableTreeNode("我的
公文包");
DefaultMutableTreeNode node2 = new DefaultMutableTreeNode("我的
電腦");
DefaultMutableTreeNode node3 = new DefaultMutableTreeNode("收藏夾");
DefaultMutableTreeNode node4 = new DefaultMutableTreeNode("Readme");
DefaultTreeModel treeModel = new DefaultTreeModel(root);
/*DefaultTreeModel類所提供的insertNodeInto()方法加入節點到父節點
的數量.
*利用DefaultMutableTreeNode類所提供的getChildCount()方法取得目
前子節點的數量.
*/
treeModel.insertNodeInto(node1, root, root.getChildCount());
treeModel.insertNodeInto(node2, root, root.getChildCount());
treeModel.insertNodeInto(node3, root, root.getChildCount());
treeModel.insertNodeInto(node4, root, root.getChildCount());
DefaultMutableTreeNode leafnode = new
DefaultMutableTreeNode("公司文件");
//DefaultTreeModel類所提供的insertNodeInto()方法加入節點到父節點
的數量.
treeModel.insertNodeInto(leafnode, node1, node1.getChildCount());
leafnode = new DefaultMutableTreeNode("個人信件");
treeModel.insertNodeInto(leafnode, node1, node1.getChildCount());
leafnode = new DefaultMutableTreeNode("私人文件");
treeModel.insertNodeInto(leafnode, node1, node1.getChildCount());
leafnode = new DefaultMutableTreeNode("本機磁盤(C:)");
treeModel.insertNodeInto(leafnode, node2, node2.getChildCount());
leafnode = new DefaultMutableTreeNode("本機磁盤(D:)");
treeModel.insertNodeInto(leafnode, node2, node2.getChildCount());
leafnode = new DefaultMutableTreeNode("本機磁盤(E:)");
treeModel.insertNodeInto(leafnode, node2, node2.getChildCount());
DefaultMutableTreeNode node31 = new DefaultMutableTreeNode("網站
列表");
treeModel.insertNodeInto(node31, node3, node3.getChildCount());
leafnode = new DefaultMutableTreeNode("奇摩站");
treeModel.insertNodeInto(leafnode, node3, node3.getChildCount());
leafnode = new DefaultMutableTreeNode("職棒消息");
treeModel.insertNodeInto(leafnode, node3, node3.getChildCount());
leafnode = new DefaultMutableTreeNode("網絡書店");
treeModel.insertNodeInto(leafnode, node3, node3.getChildCount());
try {
LookAndFeel alloyLnF = new AlloyLookAndFeel();
UIManager.setLookAndFeel(alloyLnF);
} catch (UnsupportedLookAndFeelException ex) {
// You may handle the exception here
}
// this line needs to be implemented in order to make JWS work
properly
UIManager.getLookAndFeelDefaults().put("ClassLoader",
getClass().getClassLoader());
//以TreeModel建立JTree。
JTree tree = new JTree(treeModel);
/*改變JTree的外觀**/
tree.putClientProperty("JTree.lineStyle","Horizontal");
/*改變JTree的外觀**/
JScrollPane scrollPane = new JScrollPane();
scrollPane.setViewportView(tree);
contentPane.add(scrollPane);
f.pack();
f.setVisible(true);
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
public static void main(String args[]) {
new TreeDemo3();
}
}
10-5:改變JTree的外觀:
你可以使用JComponent所提供的putClientProperty(Object key,Object value)
方法來設置java預設的JTree外觀,設置方式共有
3種:
1.tree.putClientProperty("JTree.lineStyle","None"):java預設值。
2.tree.putClientProperty("JTree.lineStyle","Horizontal"):使JTree的文件夾
間具有水平分隔線。
3.tree.putClientProperty("JTree.lineStyle","Angled"):使JTree具有類似
Windows文件管理器的直角連接線。
具體怎樣做,可看上例.
10-6:更換JTree節點圖案:
JTree利用TreeCellRenderer界面來運行繪製節點的工作,同樣的,你不需要直
接支實作這個界面所定義的方法,因為java本身提
供一個已經實作好的類來給我們使用,此類就是DefaultTreeCellRenderer,你可以
在javax.swing.tree package中找到此類所提供
的方法。下面為使用DefaultTreeCellRenderer更改節點圖案的一個例子:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.tree.*;
import com.incors.plaf.alloy.*;
public class TreeDemo4{
public TreeDemo4(){
JFrame f=new JFrame("TreeDemo");
Container contentPane=f.getContentPane();
DefaultMutableTreeNode root = new DefaultMutableTreeNode("資源管
理器");
DefaultMutableTreeNode node1 = new DefaultMutableTreeNode("我的
公文包");
DefaultMutableTreeNode node2 = new DefaultMutableTreeNode("我的
電腦");
DefaultMutableTreeNode node3 = new DefaultMutableTreeNode("收藏夾");
DefaultMutableTreeNode node4 = new DefaultMutableTreeNode("Readme");
DefaultTreeModel treeModel = new DefaultTreeModel(root);
treeModel.insertNodeInto(node1, root, root.getChildCount());
treeModel.insertNodeInto(node2, root, root.getChildCount());
treeModel.insertNodeInto(node3, root, root.getChildCount());
treeModel.insertNodeInto(node4, root, root.getChildCount());
DefaultMutableTreeNode leafnode = new
DefaultMutableTreeNode("公司文件");
treeModel.insertNodeInto(leafnode, node1, node1.getChildCount());
leafnode = new DefaultMutableTreeNode("個人信件");
treeModel.insertNodeInto(leafnode, node1, node1.getChildCount());
leafnode = new DefaultMutableTreeNode("私人文件");
treeModel.insertNodeInto(leafnode, node1, node1.getChildCount());
leafnode = new DefaultMutableTreeNode("本機磁盤(C:)");
treeModel.insertNodeInto(leafnode, node2, node2.getChildCount());
leafnode = new DefaultMutableTreeNode("本機磁盤(D:)");
treeModel.insertNodeInto(leafnode, node2, node2.getChildCount());
leafnode = new DefaultMutableTreeNode("本機磁盤(E:)");
treeModel.insertNodeInto(leafnode, node2, node2.getChildCount());
DefaultMutableTreeNode node31 = new DefaultMutableTreeNode("網站
列表");
treeModel.insertNodeInto(node31, node3, node3.getChildCount());
leafnode = new DefaultMutableTreeNode("奇摩站");
treeModel.insertNodeInto(leafnode, node3, node3.getChildCount());
leafnode = new DefaultMutableTreeNode("職棒消息");
treeModel.insertNodeInto(leafnode, node3, node3.getChildCount());
leafnode = new DefaultMutableTreeNode("網絡書店");
treeModel.insertNodeInto(leafnode, node3, node3.getChildCount());
try {
LookAndFeel alloyLnF = new AlloyLookAndFeel();
UIManager.setLookAndFeel(alloyLnF);
} catch (UnsupportedLookAndFeelException ex) {
// You may handle the exception here
}
// this line needs to be implemented in order to make JWS work
properly
UIManager.getLookAndFeelDefaults().put("ClassLoader",
getClass().getClassLoader());
JTree tree = new JTree(treeModel);
tree.setRowHeight(20);
DefaultTreeCellRenderer
cellRenderer=(DefaultTreeCellRenderer)tree.getCellRenderer();
cellRenderer.setLeafIcon(new ImageIcon("..//icons//leaf.gif"));
cellRenderer.setOpenIcon(new ImageIcon("..//icons//open.gif"));
cellRenderer.setClosedIcon(new ImageIcon("..//icons//close.gif"));
cellRenderer.setFont(new Font("宋體",Font.PLAIN,12));//設置字體.
cellRenderer.setBackgroundNonSelectionColor(Color.white);
cellRenderer.setBackgroundSelectionColor(Color.yellow);
cellRenderer.setBorderSelectionColor(Color.red);
/*設置選時或不選時,文字的變化顏色
*/
cellRenderer.setTextNonSelectionColor(Color.black);
cellRenderer.setTextSelectionColor(Color.blue);
JScrollPane scrollPane=new JScrollPane();
scrollPane.setViewportView(tree);
contentPane.add(scrollPane);
f.pack();
f.setVisible(true);
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
public static void main(String args[]) {
new TreeDemo4();
}
}
Window Xp界面:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.tree.*;
import com.incors.plaf.alloy.*;
public class TreeDemo3
{
public TreeDemo3()
{
//設置成Alloy界面樣式
try {
AlloyLookAndFeel.setProperty("alloy.isLookAndFeelFrameDecoration", "true");
LookAndFeel alloyLnF = new AlloyLookAndFeel();
JFrame.setDefaultLookAndFeelDecorated(true);
UIManager.setLookAndFeel(alloyLnF);
} catch (UnsupportedLookAndFeelException ex) {
// You may handle the exception here
}
// this line needs to be implemented in order to make JWS work
properly
UIManager.getLookAndFeelDefaults().put("ClassLoader",
getClass().getClassLoader());
//JDialog.setDefaultLookAndFeelDecorated(true);
JFrame f = new JFrame("firstTree");
Container contentPane = f.getContentPane();
// if (contentPane instanceof JComponent) {
// ((JComponent) contentPane).setMinimumSize(new
Dimension(100, 100));
//}
// Container contentPane = f.getContentPane();
DefaultMutableTreeNode root = new DefaultMutableTreeNode("資源管
理器");
DefaultMutableTreeNode node1 = new DefaultMutableTreeNode("我的
公文包");
DefaultMutableTreeNode node2 = new DefaultMutableTreeNode("我的
電腦");
DefaultMutableTreeNode node3 = new DefaultMutableTreeNode("收藏夾");
DefaultMutableTreeNode node4 = new DefaultMutableTreeNode("Readme");
DefaultTreeModel treeModel = new DefaultTreeModel(root);
treeModel.insertNodeInto(node1, root, root.getChildCount());
treeModel.insertNodeInto(node2, root, root.getChildCount());
treeModel.insertNodeInto(node3, root, root.getChildCount());
treeModel.insertNodeInto(node4, root, root.getChildCount());
DefaultMutableTreeNode leafnode = new
DefaultMutableTreeNode("公司文件");
treeModel.insertNodeInto(leafnode, node1, node1.getChildCount());
leafnode = new DefaultMutableTreeNode("個人信件");
treeModel.insertNodeInto(leafnode, node1, node1.getChildCount());
leafnode = new DefaultMutableTreeNode("私人文件");
treeModel.insertNodeInto(leafnode, node1, node1.getChildCount());
leafnode = new DefaultMutableTreeNode("本機磁盤(C:)");
treeModel.insertNodeInto(leafnode, node2, node2.getChildCount());
leafnode = new DefaultMutableTreeNode("本機磁盤(D:)");
treeModel.insertNodeInto(leafnode, node2, node2.getChildCount());
leafnode = new DefaultMutableTreeNode("本機磁盤(E:)");
treeModel.insertNodeInto(leafnode, node2, node2.getChildCount());
DefaultMutableTreeNode node31 = new DefaultMutableTreeNode("網站
列表");
treeModel.insertNodeInto(node31, node3, node3.getChildCount());
leafnode = new DefaultMutableTreeNode("奇摩站");
treeModel.insertNodeInto(leafnode, node3, node3.getChildCount());
leafnode = new DefaultMutableTreeNode("職棒消息");
treeModel.insertNodeInto(leafnode, node3, node3.getChildCount());
leafnode = new DefaultMutableTreeNode("網絡書店");
treeModel.insertNodeInto(leafnode, node3, node3.getChildCount());
JTree tree = new JTree(treeModel);
/*改變JTree的外觀**/
// tree.putClientProperty("JTree.lineStyle","Horizontal");
/*改變JTree的外觀**/
JScrollPane scrollPane = new JScrollPane();
scrollPane.setViewportView(tree);
contentPane.add(scrollPane);
f.pack();
f.setVisible(true);
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
public static void main(String args[]) {
new TreeDemo3();
}
}
10-7:JTree的事件處理模式:
在此節中,我們將詳細介紹JTree兩個常用的事件與處理,分別是
TreeModeEvent與TreeSelectionEvent.
10-7-1:處理TreeModeEvent事件:
當樹的結構上有任何改變時,例如節點值改變了、新增節點、刪除節點等,都會
TreeModelEvent事件,要處理這樣的事件必須實
作TreeModelListener界面,此界面定義了4個方法,如下所示:
TreeModelListener方法:
Void treeNodesChanged(TreeModelEvent e):當節點改變時系統就
會雲調用這個方法。
Void treeNodesInserted(TreeModelEvent e):當新增節時系統就會
去調用這個方法。
Void treeNodesRemoved(TreeModeEvent e):當刪除節點時系統就會
去調用這個方法。
Void treeStructureChanged(TreeModelEvent e):當樹結構改變時
系統就會去調用這個方法。
TreeModelEvent類本身提供了5個方法,幫我們取得事件的信息,如下所示:
TreeModelEvent方法:
int[] getChildIndices():返回子節點群的索引值。
Object[] getChildren():返回子節點群.
Object[] getPath():返回Tree中一條path上(從root nod到leaf
node)的節點。
TreePath getTreePath():取得目前位置的Tree Path.
String toString():取得蝗字符串表示法.
由TreeModelEvent的getTreePath()方法就可以得到TreePath對象,此對象就
能夠讓我們知道用戶目前正選哪一個節點,
TreePath類最常用的方法為:
public Object getLastPathComponent():取得最深(內)層的節點。
public int getPathCount():取得此path上共有幾個節點.
我們來看下面這個例子,用戶可以在Tree上編輯節點,按下[Enter]鍵後就可
以改變原有的值,並將改變的值顯示在下面的
JLabel中:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.tree.*;
import com.incors.plaf.alloy.*;
import com.incors.plaf.alloy.themes.bedouin.*;
public class TreeDemo5 implements TreeModelListener
{
JLabel label = null;
String nodeName = null; //原有節點名稱
public TreeDemo5()
{
try {
AlloyLookAndFeel.setProperty("alloy.isLookAndFeelFrameDecoration", "true");
AlloyTheme theme = new BedouinTheme();//設置界面的外觀,手冊中
共有5種樣式
LookAndFeel alloyLnF = new AlloyLookAndFeel(theme);
UIManager.setLookAndFeel(alloyLnF);
} catch (UnsupportedLookAndFeelException ex) {
// You may handle the exception here
}
// this line needs to be implemented in order to make JWS work
properly
UIManager.getLookAndFeelDefaults().put("ClassLoader",
getClass().getClassLoader());
JFrame f = new JFrame("TreeDemo");
Container contentPane = f.getContentPane();
DefaultMutableTreeNode root = new DefaultMutableTreeNode("資源管
理器");
DefaultMutableTreeNode node1 = new DefaultMutableTreeNode("文件夾");
DefaultMutableTreeNode node2 = new DefaultMutableTreeNode("我的
電腦");
DefaultMutableTreeNode node3 = new DefaultMutableTreeNode("收藏夾");
DefaultMutableTreeNode node4 = new DefaultMutableTreeNode("Readme");
root.add(node1);
root.add(node2);
root.add(node3);
root.add(node4);
DefaultMutableTreeNode leafnode = new DefaultMutableTreeNode("公
司文件");
node1.add(leafnode);
leafnode = new DefaultMutableTreeNode("個人信件");
node1.add(leafnode);
leafnode = new DefaultMutableTreeNode("私人文件");
node1.add(leafnode);
leafnode = new DefaultMutableTreeNode("本機磁盤(C:)");
node2.add(leafnode);
leafnode = new DefaultMutableTreeNode("本機磁盤(D:)");
node2.add(leafnode);
leafnode = new DefaultMutableTreeNode("本機磁盤(E:)");
node2.add(leafnode);
DefaultMutableTreeNode node31 = new DefaultMutableTreeNode("網站
列表");
node3.add(node31);
leafnode = new DefaultMutableTreeNode("天勤網站");
node31.add(leafnode);
leafnode = new DefaultMutableTreeNode("足球消息");
node31.add(leafnode);
leafnode = new DefaultMutableTreeNode("網絡書店");
node31.add(leafnode);
JTree tree = new JTree(root);
tree.setEditable(true);//設置JTree為可編輯的
tree.addMouseListener(new MouseHandle());//使Tree加入檢測Mouse事
件,以便取得節點名稱
//下面兩行取得DefaultTreeModel,並檢測是否有TreeModelEvent事件.
DefaultTreeModel treeModel = (DefaultTreeModel)tree.getModel();
treeModel.addTreeModelListener(this);
JScrollPane scrollPane = new JScrollPane();
scrollPane.setViewportView(tree);
label = new JLabel("更改數據為: ");
contentPane.add(scrollPane,BorderLayout.CENTER);
contentPane.add(label,BorderLayout.SOUTH);
f.pack();
f.setVisible(true);
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
/*本方法實作TreeModelListener介面,本介面共定義四個方法,分別是
TreeNodesChanged()
*treeNodesInserted()、treeNodesRemoved()、treeNodesRemoved()、
*treeStructureChanged().在此範例中我們只針對更改節點值的部份,因此
只實作
*treeNodesChanged()方法.
*/
public void treeNodesChanged(TreeModelEvent e) {
TreePath treePath = e.getTreePath();
System.out.println(treePath);
//下面這行由TreeModelEvent取得的DefaultMutableTreeNode為節點的父
節點,而不是用戶點選
//的節點,這點讀者要特別注意。要取得真正的節點需要再加寫下面6行代碼.
DefaultMutableTreeNode node =
(DefaultMutableTreeNode)treePath.getLastPathComponent();
try {
//getChildIndices()方法會返回目前修改節點的索引值。由於我們
只修改一個節點,因此節點索引值就放在index[0]
//的位置,若點選的節點為root node,則getChildIndices()的返回
值為null,程式下面的第二行就在處理點選root
//node產生的NullPointerException問題.
int[] index = e.getChildIndices();
//由DefaultMutableTreeNode類的getChildAt()方法取得修改的節
點對象.
node = (DefaultMutableTreeNode)node.getChildAt(index[0]);
} catch (NullPointerException exc) {}
//由DefaultMutableTreeNode類getUserObject()方法取得節點的內容,
或是寫成node.toString()亦相同.
label.setText(nodeName+"更改數據為: "+(String)node.getUserObject());
}
public void treeNodesInserted(TreeModelEvent e) {
}
public void treeNodesRemoved(TreeModelEvent e) {
}
public void treeStructureChanged(TreeModelEvent e) {
}
public static void main(String args[]) {
new TreeDemo5();
}
//處理Mouse點選事件
class MouseHandle extends MouseAdapter
{
public void mousePressed(MouseEvent e)
{
try{
JTree tree = (JTree)e.getSource();
//JTree的getRowForLocation()方法會返回節點的列索引值。例如本例
中,“本機磁盤(D:)”的列索引值為4,此索引值
//會隨着其他數據夾的打開或收起而變支,但“資源管理器”的列索引值恆為0.
int rowLocation = tree.getRowForLocation(e.getX(), e.getY());
/*JTree的getPathForRow()方法會取得從root node到點選節點的一
條path,此path為一條直線,如程式運行的圖示
*若你點選“本機磁盤(E:)”,則Tree Path為"資源管理器"-->"我的
電腦"-->"本機磁盤(E:)",因此利用TreePath
*的getLastPathComponent()方法就可以取得所點選的節點.
*/
TreePath treepath = tree.getPathForRow(rowLocation);
TreeNode treenode = (TreeNode)
treepath.getLastPathComponent();
nodeName = treenode.toString();
}catch(NullPointerException ne){}
}
}
}
注:上面的程式MouseHandle中:
int rowLocation = tree.getRowForLocation(e.getX(), e.getY());
TreePath treepath = tree.getPathForRow(rowLocation);
與:
TreePath treepath=tree.getSelectionPath();
等價,可互換。
我們將“我的電腦”改成“網上領居”:
我們再來看一個TreeModelEvent的例子,下面這個例子我們可以讓用戶自行增
加、刪除與修改節點:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.tree.*;
import com.incors.plaf.alloy.*;
import com.incors.plaf.alloy.themes.bedouin.*;
public class TreeDemo6 implements ActionListener,TreeModelListener{
JLabel label=null;
JTree tree=null;
DefaultTreeModel treeModel=null;
String nodeName=null;//原有節點名稱
public TreeDemo6(){
try {
AlloyLookAndFeel.setProperty("alloy.isLookAndFeelFrameDecoration", "true");
AlloyTheme theme = new BedouinTheme();
LookAndFeel alloyLnF = new AlloyLookAndFeel(theme);
UIManager.setLookAndFeel(alloyLnF);
} catch (UnsupportedLookAndFeelException ex) {
// You may handle the exception here
}
// this line needs to be implemented in order to make JWS work
properly
UIManager.getLookAndFeelDefaults().put("ClassLoader",
getClass().getClassLoader());
JFrame f=new JFrame("TreeDemo6");
Container contentPane=f.getContentPane();
DefaultMutableTreeNode root=new DefaultMutableTreeNode("資源管理器");
tree=new JTree(root);
tree.setEditable(true);
tree.addMouseListener(new MouseHandle());
treeModel=(DefaultTreeModel)tree.getModel();
treeModel.addTreeModelListener(this);
JScrollPane scrollPane=new JScrollPane();
scrollPane.setViewportView(tree);
JPanel panel=new JPanel();
JButton b=new JButton("新增節點");
b.addActionListener(this);
panel.add(b);
b=new JButton("刪除節點");
b.addActionListener(this);
panel.add(b);
b=new JButton("清除所有節點");
b.addActionListener(this);
panel.add(b);
label=new JLabel("Action");
contentPane.add(panel,BorderLayout.NORTH);
contentPane.add(scrollPane,BorderLayout.CENTER);
contentPane.add(label,BorderLayout.SOUTH);
f.pack();
f.setVisible(true);
f.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e){
System.exit(0);
}
});
}
//本方法運行新增、刪除、清除所有節點的程式代碼.
public void actionPerformed(ActionEvent ae){
if (ae.getActionCommand().equals("新增節點")){
DefaultMutableTreeNode parentNode=null;
DefaultMutableTreeNode newNode=new DefaultMutableTreeNode("新節點");
newNode.setAllowsChildren(true);
TreePath parentPath=tree.getSelectionPath();
//取得新節點的父節點
parentNode=(DefaultMutableTreeNode)(parentPath.getLastPathComponent());
//由DefaultTreeModel的insertNodeInto()方法增加新節點
treeModel.insertNodeInto(newNode,parentNode,parentNode.getChildCount());
//tree的scrollPathToVisible()方法在使Tree會自動展開文件夾以便顯
示所加入的新節點。若沒加這行則加入的新節點
//會被 包在文件夾中,你必須自行展開文件夾才看得到。
tree.scrollPathToVisible(new TreePath(newNode.getPath()));
label.setText("新增節點成功");
}
if (ae.getActionCommand().equals("刪除節點")){
TreePath treepath=tree.getSelectionPath();
if (treepath!=null){
//下面兩行取得選取節點的父節點.
DefaultMutableTreeNode
selectionNode=(DefaultMutableTreeNode)treepath.getLastPathComponent();
TreeNode parent=(TreeNode)selectionNode.getParent();
if (parent!=null) {
//由DefaultTreeModel的removeNodeFromParent()方法刪除節點,
包含它的子節點。
treeModel.removeNodeFromParent(selectionNode);
label.setText("刪除節點成功");
}
}
}
if (ae.getActionCommand().equals("清除所有節點")){
//下面一行,由DefaultTreeModel的getRoot()方法取得根節點.
DefaultMutableTreeNode
rootNode=(DefaultMutableTreeNode)treeModel.getRoot();
//下面一行刪除所有子節點.
rootNode.removeAllChildren();
//刪除完後務必運行DefaultTreeModel的reload()操作,整個Tree的節點
才會真正被刪除.
treeModel.reload();
label.setText("清除所有節點成功");
}
}
public void treeNodesChanged(TreeModelEvent e){
TreePath treePath=e.getTreePath();
DefaultMutableTreeNode
node=(DefaultMutableTreeNode)treePath.getLastPathComponent();
try{
int[] index=e.getChildIndices();
node=(DefaultMutableTreeNode)node.getChildAt(index[0]);
}catch(NullPointerException exc){}
label.setText(nodeName+"更改數據為:"+(String)node.getUserObject());
}
public void treeNodesInserted(TreeModelEvent e){
System.out.println("new node insered");
}
public void treeNodesRemoved(TreeModelEvent e){
System.out.println("node deleted");
}
public void treeStructureChanged(TreeModelEvent e){
System.out.println("Structrue changed");
}
public static void main(String[] args){
new TreeDemo6();
}
class MouseHandle extends MouseAdapter{
public void mousePressed(MouseEvent e){
try{
JTree tree=(JTree)e.getSource();
int rowLocation=tree.getRowForLocation(e.getX(),e.getY());
TreePath treepath=tree.getPathForRow(rowLocation);
TreeNode treenode=(TreeNode)treepath.getLastPathComponent();
nodeName=treenode.toString();
}catch(NullPointerException ne){}
}
}
}
10-7-2:處理TreeSelectionEvent事件:
當我們在JTree上點選任何一個節點,都會觸發TreeSelectionEvent事件,如
果我們要處理這樣的事件,必須實作
TreeSelectionListener界面,此界面只定義了一個方法,那就是valueChanged()
方法。
TreeSelectionEvent最常用在處理顯示節點的內容,例如你在文件圖標中點兩
下就可以看到文件的內容。在JTree中選擇節點
的方式共有3種,這3種情況跟選擇JList上的項目是一模一樣的,分別是:
DISCONTIGUOUS_TREE_SELECTION:可作單一選擇,連續點選擇(按住[Shift]
鍵),不連續選擇多個節點(按住[Ctrl]鍵),
這是java預設值.
CONTINUOUS_TREE_SELECTION:按住[Shift]鍵,可對某一連續的節點區間作
選取。
SINGLE_TREE_SELECTION:一次只能選一個節點。
你可以自行實作TreeSelectionModel製作作更複雜的選擇方式,但通常是沒有
必要的,因為java提供了預設的選擇模式類供我們
使用,那就是DefaultTreeSelectionModel,利用這個類我們可以很方便的設置上面
3種選擇模式。
下面這個範例,當用戶點選了一個文件名時,就會將文件的內容顯示出來。
TreeDemo7.java
import javax.swing.*;
import javax.swing.tree.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;
import com.incors.plaf.alloy.*;
import com.incors.plaf.alloy.themes.glass.*;
public class TreeDemo7 implements TreeSelectionListener
{
JEditorPane editorPane;
public TreeDemo7()
{
JFrame f = new JFrame("TreeDemo");
Container contentPane = f.getContentPane();
DefaultMutableTreeNode root = new DefaultMutableTreeNode("資源管
理器");
DefaultMutableTreeNode node = new
DefaultMutableTreeNode("TreeDemo1.java");
root.add(node);
node = new DefaultMutableTreeNode("TreeDemo2.java");
root.add(node);
node = new DefaultMutableTreeNode("TreeDemo3.java");
root.add(node);
node = new DefaultMutableTreeNode("TreeDemo4.java");
root.add(node);
JTree tree = new JTree(root);
//設置Tree的選擇模式為一次只能選擇一個節點
tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
//檢查是否有TreeSelectionEvent事件。
tree.addTreeSelectionListener(this);
//下面五行,JSplitPane中,左邊是放含有JTree的JScrollPane,右邊是
放JEditorPane.
JScrollPane scrollPane1 = new JScrollPane(tree);
editorPane = new JEditorPane();
JScrollPane scrollPane2 = new JScrollPane(editorPane);
JSplitPane splitPane = new JSplitPane(
JSplitPane.HORIZONTAL_SPLIT,true, scrollPane1, scrollPane2);
contentPane.add(splitPane);
f.pack();
f.setVisible(true);
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
//本方法實作valueChanged()方法
public void valueChanged(TreeSelectionEvent e)
{
JTree tree = (JTree) e.getSource();
//利用JTree的getLastSelectedPathComponent()方法取得目前選取的節點.
DefaultMutableTreeNode selectionNode =
(DefaultMutableTreeNode)tree.getLastSelectedPathComponent();
String nodeName = selectionNode.toString();
//判斷是否為樹葉節點,若是則顯示文件內容,若不是則不做任何事。
if (selectionNode.isLeaf())
{
/*取得文件的位置路徑,System.getProperty("user.dir")可以取得目
前工作的路徑,
*System.getProperty("file.separator")是取得文件分隔符,例如
在window環境的
*文件分陋符是"/",而Unix環境的文件分隔符剛好相反,是"/".利用
System.getProperty()
*方法你可以取得下列的信息:
java.version 顯示java版本
java.endor 顯示java製造商
java.endor.url 顯示java製造商URL
java.home 顯示java的安裝路徑
java.class.version 顯示java類版本
java.class.path 顯示java classpath
os.name 顯示操作系統名稱
os.arch 顯示操作系統結構,如x86
os.version 顯示操作系統版本
file.separator 取得文件分隔符
path.separator 取得路徑分隔符,如
Unix是以“:”表示
line.separator 取得換行符號,如
Unix是以"/n"表示
user.name 取得用戶名稱
user.home 取得用戶家目錄(home
directory),如Windows中Administrator的家目
錄為c:/Documents
and Settings/Administrator
user.dir 取得用戶目前的工作目錄.
*/
String filepath = "file:"+System.getProperty("user.dir") +
System.getProperty("file.separator") +
nodeName;
try {
//利用JEditorPane的setPage()方法將文件內容顯示在
editorPane中。若文件路徑錯誤,則會產生IOException.
editorPane.setPage(filepath);
} catch(IOException ex) {
System.out.println("找不到此文件");
}
}
}
public static void main(String[] args) {
SwingUtil.setLookAndFeel();
new TreeDemo7();
}
}
class SwingUtil{
public static final void setLookAndFeel() {
try{
Font font = new Font("JFrame", Font.PLAIN, 12);
Enumeration keys = UIManager.getLookAndFeelDefaults().keys();
while (keys.hasMoreElements()) {
Object key = keys.nextElement();
if (UIManager.get(key) instanceof Font) {
UIManager.put(key, font);
}
}
AlloyLookAndFeel.setProperty("alloy.isLookAndFeelFrameDecoration", "true");
AlloyTheme theme = new GlassTheme();
LookAndFeel alloyLnF = new AlloyLookAndFeel(theme);
JFrame.setDefaultLookAndFeelDecorated(true);
UIManager.setLookAndFeel(alloyLnF);
}catch(UnsupportedLookAndFeelException ex){
ex.printStackTrace();
}
}
}
10-8:JTree的其他操作:
我們在之前小節中曾說到Tree中的每一個節點都是一個TreeNode,並可利用
JTree的setEditable()方法設置節點是否可編輯,
若要在Tree中找尋節點的父節點或子節點,或判斷是否為樹節點,皆可由實作
TreeNode界面做到,但要編輯節點呢?java將編輯
節點的任務交給TreeCellEditor,TreeCellEditor本身是一個界面,裡面只定義了
getTreeCellEditor Component()方法,你可以實
作此方法使節點具有編輯的效果。不過你不用這麼辛苦去實作這個方法,java本身
提供了DefaultTreeCellEditor類來實作此方法
,亦提供了其他許多方法,例如取得節點內容(getCellEditorValue()) 、設置節
點字體(setFont())、決定節點是否可編輯
(isCellEditable())等等。除非你覺得DefaultTreeCellEditor所提供的功能不
夠,你才需要去實作TreeCellEditor界面。你可以利
用JTree的getCellEditor()方法取得DefaultTreeCellEditor對象。當我們編輯節
點時會觸發ChangeEvent事件,你可以實作
CellEditorListener界面來處理此事件,CellEditorListener界面包括兩個方法,
分別是editingStopped(ChangeEvent e)與
editingCanceled(ChangeEvent e).若你沒有實作TreeCellEditor界面,系統會以
預設的DefaultTreeCellEdtior類來處理掉這兩個
方法(你可以在DefaultTreeCellEditor中找到這兩個方法),因此你無須再編寫任
何的程式。
另外,JTree還有一種事件處理模式,那就是TreeExpansionEvent事件。要處
理這個事件你必須實作TreeExpansionListener
界面,此界面定義了兩個方法,分別是treeCollapsed(TreeExpansionEvent e)與
treeExpanded(TreeExpansionEvent e).當節點展
開時系統就會自動調用treeExpanded()方法,當節點合起來時,系統就會自動調用
treeCollapsed()方法。你可以在這兩個方法中編寫所要處理事情的程式碼。
10-1:使用JTree組件:
java.lang.Object
--java.awt.Component
--java.awt.Container
--javax.swing.JComponent
--javax.swing.JTree
JTree構造函數:
JTree():建立一棵系統預設的樹。
JTree(Hashtable value):利用Hashtable建立樹,不顯示root node(根節點).
JTree(Object[] value):利用Object Array建立樹,不顯示root node.
JTree(TreeModel newModel):利用TreeModel建立樹。
JTree(TreeNode root):利用TreeNode建立樹。
JTree(TreeNode root,boolean asksAllowsChildren):利用TreeNode建立樹,並決
定是否允許子節點的存在.
JTree(Vector value):利用Vector建立樹,不顯示root node.
範例:
InitalTree.java
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class InitalTree{
public InitalTree(){
JFrame f=new JFrame("TreeDemo");
Container contentPane=f.getContentPane();
JTree tree=new JTree();
JScrollPane scrollPane=new JScrollPane();
scrollPane.setViewportView(tree);
contentPane.add(scrollPane);
f.pack();
f.setVisible(true);
f.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e){
System.exit(0);
}
});
}
public static void main(String[] args){
new InitalTree();
}
}
10-2:以Hashtable構造JTree:
上面的例子對我們並沒有裨的幫助,因為各個節點的數據均是java的預設值,
而非我們自己設置的。因此我們需利用其他JTree
構造函數來輸入我們想要的節點數據。以下範例我們以Hashtable當作JTree的數據
輸入:
範例:TreeDemo1.java
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
public class TreeDemo1{
public TreeDemo1(){
JFrame f=new JFrame("TreeDemo1");
Container contentPane=f.getContentPane();
String[] s1={"公司文件","個人信件","私人文件"};
String[] s2={"本機磁盤(C:)","本機磁盤(D:)","本機磁盤(E:)"};
String[] s3={"奇摩站","職棒消息","網絡書店"};
Hashtable hashtable1=new Hashtable();
Hashtable hashtable2=new Hashtable();
hashtable1.put("我的公文包",s1);
hashtable1.put("我的電腦",s2);
hashtable1.put("收藏夾",hashtable2);
hashtable2.put("網站列表",s3);
Font font = new Font("Dialog", Font.PLAIN, 12);
Enumeration keys = UIManager.getLookAndFeelDefaults().keys();
/**定義widnows界面**/
while (keys.hasMoreElements()) {
Object key = keys.nextElement();
if (UIManager.get(key) instanceof Font) {
UIManager.put(key, font);
}
}
try{
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
}catch(Exception el){
System.exit(0);
}
/**定義widnows界面**/
JTree tree=new JTree(hashtable1);
JScrollPane scrollPane=new JScrollPane();
scrollPane.setViewportView(tree);
contentPane.add(scrollPane);
f.pack();
f.setVisible(true);
f.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e){
System.exit(0);
}
});
}
public static void main(String[] args){
new TreeDemo1();
}
}
純XP界面的設置:
10-3:以TreeNode構造JTree:
JTree上的每一個節點就代表一個TreeNode對象,TreeNode本身是一個
Interface,裡面定義了7個有關節點的方法,例如判斷是否
為樹葉節點、有幾個子節點(getChildCount())、父節點為何(getparent())等等、
這些方法的定義你可以在javax.swing.tree的
package中找到,讀者可自行查閱java api文件。在實際的應用上,一般我們不會
直接實作此界面,而是採用java所提供的
DefaultMutableTreeMode類,此類是實作MutableTreeNode界面而來,並提供了其
他許多實用的方法。MutableTreeNode本身也是一
個Interface,且繼承了TreeNode界面此類主要是定義一些節點的處理方式,例如新
增節點(insert())、刪除節點(remove())、設置
節點(setUserObject())等。整個關係如下圖:
TreeNode----extends--->MutableTreeNode---implements---DefaultMutableTreeNode
接下來我們來看如何利DefaultMutableTreeNode來建立JTree,我們先來看
DefaultMutableTreeNode的構造函數:
DefaultMutableTreeNode構造函數:
DefaultMutableTreeNode():建立空的DefaultMutableTreeNode對象。
DefaultMutableTreeNode(Object userObject):建立DefaultMutableTreeNode對
象,節點為userObject對象。
DefaultMutableTreeNode(Object userObject,Boolean allowsChildren):建立
DefaultMutableTreeNode對象,節點為userObject對
象並決定此節點是否允許具有子節點。
以下為利用DefaultMutableTreeNode建立JTree的範例:TreeDemo2.java
此程式"資源管理器"為此棵樹的根節點.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.tree.*;
public class TreeDemo2{
public TreeDemo2(){
JFrame f=new JFrame("TreeDemo2");
Container contentPane=f.getContentPane();
DefaultMutableTreeNode root=new DefaultMutableTreeNode("資源管理器");
DefaultMutableTreeNode node1=new DefaultMutableTreeNode("我的公文包");
DefaultMutableTreeNode node2=new DefaultMutableTreeNode("我的電腦");
DefaultMutableTreeNode node3=new DefaultMutableTreeNode("收藏夾");
DefaultMutableTreeNode node4=new DefaultMutableTreeNode("Readme");
root.add(node1);
root.add(node2);
root.add(node3);
root.add(node4);
DefaultMutableTreeNode leafnode=new DefaultMutableTreeNode("公司文件");
node1.add(leafnode);
leafnode=new DefaultMutableTreeNode("私人文件");
node1.add(leafnode);
leafnode=new DefaultMutableTreeNode("個人信件");
leafnode=new DefaultMutableTreeNode("本機磁盤(C:)");
node2.add(leafnode);
leafnode=new DefaultMutableTreeNode("本機磁盤(D:)");
node2.add(leafnode);
leafnode=new DefaultMutableTreeNode("本機磁盤(E:)");
node2.add(leafnode);
DefaultMutableTreeNode node31=new DefaultMutableTreeNode("網站列表");
node3.add(node31);
leafnode=new DefaultMutableTreeNode("奇摩站");
node31.add(leafnode);
leafnode=new DefaultMutableTreeNode("職棒消息");
node31.add(leafnode);
leafnode=new DefaultMutableTreeNode("網絡書店");
node31.add(leafnode);
JTree tree=new JTree(root);
JScrollPane scrollPane=new JScrollPane();
scrollPane.setViewportView(tree);
contentPane.add(scrollPane);
f.pack();
f.setVisible(true);
f.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e){
System.exit(0);
}
});
}
public static void main(String[] args){
new TreeDemo2();
}
}
10-4:以TreeModel構造JTree.
除了以節點的觀念(TreeNode)建立樹之外,你可以用data model的模式建立
樹。樹的data model稱為TreeModel,用此模式的好處
是可以觸發相關的樹事件,來處理樹可能產生的一些變動。TreeModel是一個
interface,裡面定義了8種方法;如果你是一個喜歡自己
動手做的人,或是你想顯示的數據格式很複雜,你可以考慮直接實作TreeModel界
面中所定義的方法來構造出JTree.TreeModel界面
的方法如下所示:
TreeModel方法:
void addTreeModelListener(TreeModelListener l):增加一個
TreeModelListener來監控TreeModelEvent事件。
Object getChild(Object parent,int index):返回子節點。
int getChildCount(Object parent):返回子節點數量.
int getIndexOfChild(Object parent,Object child):返回子節點的索引值。
Object getRoot():返回根節點。
boolean isLeaf(Object node):判斷是否為樹葉節點。
void removeTreeModelListener(TreeModelListener l):刪除
TreeModelListener。
void valueForPathChanged(TreePath path,Object newValue):當用戶改變
Tree上的值時如何應對。
你可以實作出這8種方法,然後構造出自己想要的JTree,不過在大部份的情況下
我們通常不會這樣做,畢竟要實作出這8種方法不
是件很輕鬆的事,而且java本身也提供了一個預設模式,叫做DefaultTreeModel,
這個類已經實作了TreeModel界面,也另外提供許
多實用的方法。利用這個預設模式,我們便能很方便的構造出JTree出來了。下面
為DefaultTreeModel的構造函數與範例:
DefaultTreeModel構造函數:
DefaultTreeModel(TreeNode root):建立DefaultTreeModel對象,並定出根節點。
DefaultTreeModel(TreeNode root,Boolean asksAllowsChildren):建立具有根節
點的DefaultTreeModel對象,並決定此節點是否允
許具有子節點。
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.tree.*;
import com.incors.plaf.alloy.*;//組件的下載網址http://www.incors.com
/lookandfeel/
/*將alloy.jar放在c:/j2sdk1.4.0/jre/lib/ext/目錄下.
*/
public class TreeDemo3
{
public TreeDemo3()
{
JFrame f = new JFrame("TreeDemo");
Container contentPane = f.getContentPane();
DefaultMutableTreeNode root = new DefaultMutableTreeNode("資源管
理器");
DefaultMutableTreeNode node1 = new DefaultMutableTreeNode("我的
公文包");
DefaultMutableTreeNode node2 = new DefaultMutableTreeNode("我的
電腦");
DefaultMutableTreeNode node3 = new DefaultMutableTreeNode("收藏夾");
DefaultMutableTreeNode node4 = new DefaultMutableTreeNode("Readme");
DefaultTreeModel treeModel = new DefaultTreeModel(root);
/*DefaultTreeModel類所提供的insertNodeInto()方法加入節點到父節點
的數量.
*利用DefaultMutableTreeNode類所提供的getChildCount()方法取得目
前子節點的數量.
*/
treeModel.insertNodeInto(node1, root, root.getChildCount());
treeModel.insertNodeInto(node2, root, root.getChildCount());
treeModel.insertNodeInto(node3, root, root.getChildCount());
treeModel.insertNodeInto(node4, root, root.getChildCount());
DefaultMutableTreeNode leafnode = new
DefaultMutableTreeNode("公司文件");
//DefaultTreeModel類所提供的insertNodeInto()方法加入節點到父節點
的數量.
treeModel.insertNodeInto(leafnode, node1, node1.getChildCount());
leafnode = new DefaultMutableTreeNode("個人信件");
treeModel.insertNodeInto(leafnode, node1, node1.getChildCount());
leafnode = new DefaultMutableTreeNode("私人文件");
treeModel.insertNodeInto(leafnode, node1, node1.getChildCount());
leafnode = new DefaultMutableTreeNode("本機磁盤(C:)");
treeModel.insertNodeInto(leafnode, node2, node2.getChildCount());
leafnode = new DefaultMutableTreeNode("本機磁盤(D:)");
treeModel.insertNodeInto(leafnode, node2, node2.getChildCount());
leafnode = new DefaultMutableTreeNode("本機磁盤(E:)");
treeModel.insertNodeInto(leafnode, node2, node2.getChildCount());
DefaultMutableTreeNode node31 = new DefaultMutableTreeNode("網站
列表");
treeModel.insertNodeInto(node31, node3, node3.getChildCount());
leafnode = new DefaultMutableTreeNode("奇摩站");
treeModel.insertNodeInto(leafnode, node3, node3.getChildCount());
leafnode = new DefaultMutableTreeNode("職棒消息");
treeModel.insertNodeInto(leafnode, node3, node3.getChildCount());
leafnode = new DefaultMutableTreeNode("網絡書店");
treeModel.insertNodeInto(leafnode, node3, node3.getChildCount());
try {
LookAndFeel alloyLnF = new AlloyLookAndFeel();
UIManager.setLookAndFeel(alloyLnF);
} catch (UnsupportedLookAndFeelException ex) {
// You may handle the exception here
}
// this line needs to be implemented in order to make JWS work
properly
UIManager.getLookAndFeelDefaults().put("ClassLoader",
getClass().getClassLoader());
//以TreeModel建立JTree。
JTree tree = new JTree(treeModel);
/*改變JTree的外觀**/
tree.putClientProperty("JTree.lineStyle","Horizontal");
/*改變JTree的外觀**/
JScrollPane scrollPane = new JScrollPane();
scrollPane.setViewportView(tree);
contentPane.add(scrollPane);
f.pack();
f.setVisible(true);
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
public static void main(String args[]) {
new TreeDemo3();
}
}
10-5:改變JTree的外觀:
你可以使用JComponent所提供的putClientProperty(Object key,Object value)
方法來設置java預設的JTree外觀,設置方式共有
3種:
1.tree.putClientProperty("JTree.lineStyle","None"):java預設值。
2.tree.putClientProperty("JTree.lineStyle","Horizontal"):使JTree的文件夾
間具有水平分隔線。
3.tree.putClientProperty("JTree.lineStyle","Angled"):使JTree具有類似
Windows文件管理器的直角連接線。
具體怎樣做,可看上例.
10-6:更換JTree節點圖案:
JTree利用TreeCellRenderer界面來運行繪製節點的工作,同樣的,你不需要直
接支實作這個界面所定義的方法,因為java本身提
供一個已經實作好的類來給我們使用,此類就是DefaultTreeCellRenderer,你可以
在javax.swing.tree package中找到此類所提供
的方法。下面為使用DefaultTreeCellRenderer更改節點圖案的一個例子:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.tree.*;
import com.incors.plaf.alloy.*;
public class TreeDemo4{
public TreeDemo4(){
JFrame f=new JFrame("TreeDemo");
Container contentPane=f.getContentPane();
DefaultMutableTreeNode root = new DefaultMutableTreeNode("資源管
理器");
DefaultMutableTreeNode node1 = new DefaultMutableTreeNode("我的
公文包");
DefaultMutableTreeNode node2 = new DefaultMutableTreeNode("我的
電腦");
DefaultMutableTreeNode node3 = new DefaultMutableTreeNode("收藏夾");
DefaultMutableTreeNode node4 = new DefaultMutableTreeNode("Readme");
DefaultTreeModel treeModel = new DefaultTreeModel(root);
treeModel.insertNodeInto(node1, root, root.getChildCount());
treeModel.insertNodeInto(node2, root, root.getChildCount());
treeModel.insertNodeInto(node3, root, root.getChildCount());
treeModel.insertNodeInto(node4, root, root.getChildCount());
DefaultMutableTreeNode leafnode = new
DefaultMutableTreeNode("公司文件");
treeModel.insertNodeInto(leafnode, node1, node1.getChildCount());
leafnode = new DefaultMutableTreeNode("個人信件");
treeModel.insertNodeInto(leafnode, node1, node1.getChildCount());
leafnode = new DefaultMutableTreeNode("私人文件");
treeModel.insertNodeInto(leafnode, node1, node1.getChildCount());
leafnode = new DefaultMutableTreeNode("本機磁盤(C:)");
treeModel.insertNodeInto(leafnode, node2, node2.getChildCount());
leafnode = new DefaultMutableTreeNode("本機磁盤(D:)");
treeModel.insertNodeInto(leafnode, node2, node2.getChildCount());
leafnode = new DefaultMutableTreeNode("本機磁盤(E:)");
treeModel.insertNodeInto(leafnode, node2, node2.getChildCount());
DefaultMutableTreeNode node31 = new DefaultMutableTreeNode("網站
列表");
treeModel.insertNodeInto(node31, node3, node3.getChildCount());
leafnode = new DefaultMutableTreeNode("奇摩站");
treeModel.insertNodeInto(leafnode, node3, node3.getChildCount());
leafnode = new DefaultMutableTreeNode("職棒消息");
treeModel.insertNodeInto(leafnode, node3, node3.getChildCount());
leafnode = new DefaultMutableTreeNode("網絡書店");
treeModel.insertNodeInto(leafnode, node3, node3.getChildCount());
try {
LookAndFeel alloyLnF = new AlloyLookAndFeel();
UIManager.setLookAndFeel(alloyLnF);
} catch (UnsupportedLookAndFeelException ex) {
// You may handle the exception here
}
// this line needs to be implemented in order to make JWS work
properly
UIManager.getLookAndFeelDefaults().put("ClassLoader",
getClass().getClassLoader());
JTree tree = new JTree(treeModel);
tree.setRowHeight(20);
DefaultTreeCellRenderer
cellRenderer=(DefaultTreeCellRenderer)tree.getCellRenderer();
cellRenderer.setLeafIcon(new ImageIcon("..//icons//leaf.gif"));
cellRenderer.setOpenIcon(new ImageIcon("..//icons//open.gif"));
cellRenderer.setClosedIcon(new ImageIcon("..//icons//close.gif"));
cellRenderer.setFont(new Font("宋體",Font.PLAIN,12));//設置字體.
cellRenderer.setBackgroundNonSelectionColor(Color.white);
cellRenderer.setBackgroundSelectionColor(Color.yellow);
cellRenderer.setBorderSelectionColor(Color.red);
/*設置選時或不選時,文字的變化顏色
*/
cellRenderer.setTextNonSelectionColor(Color.black);
cellRenderer.setTextSelectionColor(Color.blue);
JScrollPane scrollPane=new JScrollPane();
scrollPane.setViewportView(tree);
contentPane.add(scrollPane);
f.pack();
f.setVisible(true);
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
public static void main(String args[]) {
new TreeDemo4();
}
}
Window Xp界面:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.tree.*;
import com.incors.plaf.alloy.*;
public class TreeDemo3
{
public TreeDemo3()
{
//設置成Alloy界面樣式
try {
AlloyLookAndFeel.setProperty("alloy.isLookAndFeelFrameDecoration", "true");
LookAndFeel alloyLnF = new AlloyLookAndFeel();
JFrame.setDefaultLookAndFeelDecorated(true);
UIManager.setLookAndFeel(alloyLnF);
} catch (UnsupportedLookAndFeelException ex) {
// You may handle the exception here
}
// this line needs to be implemented in order to make JWS work
properly
UIManager.getLookAndFeelDefaults().put("ClassLoader",
getClass().getClassLoader());
//JDialog.setDefaultLookAndFeelDecorated(true);
JFrame f = new JFrame("firstTree");
Container contentPane = f.getContentPane();
// if (contentPane instanceof JComponent) {
// ((JComponent) contentPane).setMinimumSize(new
Dimension(100, 100));
//}
// Container contentPane = f.getContentPane();
DefaultMutableTreeNode root = new DefaultMutableTreeNode("資源管
理器");
DefaultMutableTreeNode node1 = new DefaultMutableTreeNode("我的
公文包");
DefaultMutableTreeNode node2 = new DefaultMutableTreeNode("我的
電腦");
DefaultMutableTreeNode node3 = new DefaultMutableTreeNode("收藏夾");
DefaultMutableTreeNode node4 = new DefaultMutableTreeNode("Readme");
DefaultTreeModel treeModel = new DefaultTreeModel(root);
treeModel.insertNodeInto(node1, root, root.getChildCount());
treeModel.insertNodeInto(node2, root, root.getChildCount());
treeModel.insertNodeInto(node3, root, root.getChildCount());
treeModel.insertNodeInto(node4, root, root.getChildCount());
DefaultMutableTreeNode leafnode = new
DefaultMutableTreeNode("公司文件");
treeModel.insertNodeInto(leafnode, node1, node1.getChildCount());
leafnode = new DefaultMutableTreeNode("個人信件");
treeModel.insertNodeInto(leafnode, node1, node1.getChildCount());
leafnode = new DefaultMutableTreeNode("私人文件");
treeModel.insertNodeInto(leafnode, node1, node1.getChildCount());
leafnode = new DefaultMutableTreeNode("本機磁盤(C:)");
treeModel.insertNodeInto(leafnode, node2, node2.getChildCount());
leafnode = new DefaultMutableTreeNode("本機磁盤(D:)");
treeModel.insertNodeInto(leafnode, node2, node2.getChildCount());
leafnode = new DefaultMutableTreeNode("本機磁盤(E:)");
treeModel.insertNodeInto(leafnode, node2, node2.getChildCount());
DefaultMutableTreeNode node31 = new DefaultMutableTreeNode("網站
列表");
treeModel.insertNodeInto(node31, node3, node3.getChildCount());
leafnode = new DefaultMutableTreeNode("奇摩站");
treeModel.insertNodeInto(leafnode, node3, node3.getChildCount());
leafnode = new DefaultMutableTreeNode("職棒消息");
treeModel.insertNodeInto(leafnode, node3, node3.getChildCount());
leafnode = new DefaultMutableTreeNode("網絡書店");
treeModel.insertNodeInto(leafnode, node3, node3.getChildCount());
JTree tree = new JTree(treeModel);
/*改變JTree的外觀**/
// tree.putClientProperty("JTree.lineStyle","Horizontal");
/*改變JTree的外觀**/
JScrollPane scrollPane = new JScrollPane();
scrollPane.setViewportView(tree);
contentPane.add(scrollPane);
f.pack();
f.setVisible(true);
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
public static void main(String args[]) {
new TreeDemo3();
}
}
10-7:JTree的事件處理模式:
在此節中,我們將詳細介紹JTree兩個常用的事件與處理,分別是
TreeModeEvent與TreeSelectionEvent.
10-7-1:處理TreeModeEvent事件:
當樹的結構上有任何改變時,例如節點值改變了、新增節點、刪除節點等,都會
TreeModelEvent事件,要處理這樣的事件必須實
作TreeModelListener界面,此界面定義了4個方法,如下所示:
TreeModelListener方法:
Void treeNodesChanged(TreeModelEvent e):當節點改變時系統就
會雲調用這個方法。
Void treeNodesInserted(TreeModelEvent e):當新增節時系統就會
去調用這個方法。
Void treeNodesRemoved(TreeModeEvent e):當刪除節點時系統就會
去調用這個方法。
Void treeStructureChanged(TreeModelEvent e):當樹結構改變時
系統就會去調用這個方法。
TreeModelEvent類本身提供了5個方法,幫我們取得事件的信息,如下所示:
TreeModelEvent方法:
int[] getChildIndices():返回子節點群的索引值。
Object[] getChildren():返回子節點群.
Object[] getPath():返回Tree中一條path上(從root nod到leaf
node)的節點。
TreePath getTreePath():取得目前位置的Tree Path.
String toString():取得蝗字符串表示法.
由TreeModelEvent的getTreePath()方法就可以得到TreePath對象,此對象就
能夠讓我們知道用戶目前正選哪一個節點,
TreePath類最常用的方法為:
public Object getLastPathComponent():取得最深(內)層的節點。
public int getPathCount():取得此path上共有幾個節點.
我們來看下面這個例子,用戶可以在Tree上編輯節點,按下[Enter]鍵後就可
以改變原有的值,並將改變的值顯示在下面的
JLabel中:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.tree.*;
import com.incors.plaf.alloy.*;
import com.incors.plaf.alloy.themes.bedouin.*;
public class TreeDemo5 implements TreeModelListener
{
JLabel label = null;
String nodeName = null; //原有節點名稱
public TreeDemo5()
{
try {
AlloyLookAndFeel.setProperty("alloy.isLookAndFeelFrameDecoration", "true");
AlloyTheme theme = new BedouinTheme();//設置界面的外觀,手冊中
共有5種樣式
LookAndFeel alloyLnF = new AlloyLookAndFeel(theme);
UIManager.setLookAndFeel(alloyLnF);
} catch (UnsupportedLookAndFeelException ex) {
// You may handle the exception here
}
// this line needs to be implemented in order to make JWS work
properly
UIManager.getLookAndFeelDefaults().put("ClassLoader",
getClass().getClassLoader());
JFrame f = new JFrame("TreeDemo");
Container contentPane = f.getContentPane();
DefaultMutableTreeNode root = new DefaultMutableTreeNode("資源管
理器");
DefaultMutableTreeNode node1 = new DefaultMutableTreeNode("文件夾");
DefaultMutableTreeNode node2 = new DefaultMutableTreeNode("我的
電腦");
DefaultMutableTreeNode node3 = new DefaultMutableTreeNode("收藏夾");
DefaultMutableTreeNode node4 = new DefaultMutableTreeNode("Readme");
root.add(node1);
root.add(node2);
root.add(node3);
root.add(node4);
DefaultMutableTreeNode leafnode = new DefaultMutableTreeNode("公
司文件");
node1.add(leafnode);
leafnode = new DefaultMutableTreeNode("個人信件");
node1.add(leafnode);
leafnode = new DefaultMutableTreeNode("私人文件");
node1.add(leafnode);
leafnode = new DefaultMutableTreeNode("本機磁盤(C:)");
node2.add(leafnode);
leafnode = new DefaultMutableTreeNode("本機磁盤(D:)");
node2.add(leafnode);
leafnode = new DefaultMutableTreeNode("本機磁盤(E:)");
node2.add(leafnode);
DefaultMutableTreeNode node31 = new DefaultMutableTreeNode("網站
列表");
node3.add(node31);
leafnode = new DefaultMutableTreeNode("天勤網站");
node31.add(leafnode);
leafnode = new DefaultMutableTreeNode("足球消息");
node31.add(leafnode);
leafnode = new DefaultMutableTreeNode("網絡書店");
node31.add(leafnode);
JTree tree = new JTree(root);
tree.setEditable(true);//設置JTree為可編輯的
tree.addMouseListener(new MouseHandle());//使Tree加入檢測Mouse事
件,以便取得節點名稱
//下面兩行取得DefaultTreeModel,並檢測是否有TreeModelEvent事件.
DefaultTreeModel treeModel = (DefaultTreeModel)tree.getModel();
treeModel.addTreeModelListener(this);
JScrollPane scrollPane = new JScrollPane();
scrollPane.setViewportView(tree);
label = new JLabel("更改數據為: ");
contentPane.add(scrollPane,BorderLayout.CENTER);
contentPane.add(label,BorderLayout.SOUTH);
f.pack();
f.setVisible(true);
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
/*本方法實作TreeModelListener介面,本介面共定義四個方法,分別是
TreeNodesChanged()
*treeNodesInserted()、treeNodesRemoved()、treeNodesRemoved()、
*treeStructureChanged().在此範例中我們只針對更改節點值的部份,因此
只實作
*treeNodesChanged()方法.
*/
public void treeNodesChanged(TreeModelEvent e) {
TreePath treePath = e.getTreePath();
System.out.println(treePath);
//下面這行由TreeModelEvent取得的DefaultMutableTreeNode為節點的父
節點,而不是用戶點選
//的節點,這點讀者要特別注意。要取得真正的節點需要再加寫下面6行代碼.
DefaultMutableTreeNode node =
(DefaultMutableTreeNode)treePath.getLastPathComponent();
try {
//getChildIndices()方法會返回目前修改節點的索引值。由於我們
只修改一個節點,因此節點索引值就放在index[0]
//的位置,若點選的節點為root node,則getChildIndices()的返回
值為null,程式下面的第二行就在處理點選root
//node產生的NullPointerException問題.
int[] index = e.getChildIndices();
//由DefaultMutableTreeNode類的getChildAt()方法取得修改的節
點對象.
node = (DefaultMutableTreeNode)node.getChildAt(index[0]);
} catch (NullPointerException exc) {}
//由DefaultMutableTreeNode類getUserObject()方法取得節點的內容,
或是寫成node.toString()亦相同.
label.setText(nodeName+"更改數據為: "+(String)node.getUserObject());
}
public void treeNodesInserted(TreeModelEvent e) {
}
public void treeNodesRemoved(TreeModelEvent e) {
}
public void treeStructureChanged(TreeModelEvent e) {
}
public static void main(String args[]) {
new TreeDemo5();
}
//處理Mouse點選事件
class MouseHandle extends MouseAdapter
{
public void mousePressed(MouseEvent e)
{
try{
JTree tree = (JTree)e.getSource();
//JTree的getRowForLocation()方法會返回節點的列索引值。例如本例
中,“本機磁盤(D:)”的列索引值為4,此索引值
//會隨着其他數據夾的打開或收起而變支,但“資源管理器”的列索引值恆為0.
int rowLocation = tree.getRowForLocation(e.getX(), e.getY());
/*JTree的getPathForRow()方法會取得從root node到點選節點的一
條path,此path為一條直線,如程式運行的圖示
*若你點選“本機磁盤(E:)”,則Tree Path為"資源管理器"-->"我的
電腦"-->"本機磁盤(E:)",因此利用TreePath
*的getLastPathComponent()方法就可以取得所點選的節點.
*/
TreePath treepath = tree.getPathForRow(rowLocation);
TreeNode treenode = (TreeNode)
treepath.getLastPathComponent();
nodeName = treenode.toString();
}catch(NullPointerException ne){}
}
}
}
注:上面的程式MouseHandle中:
int rowLocation = tree.getRowForLocation(e.getX(), e.getY());
TreePath treepath = tree.getPathForRow(rowLocation);
與:
TreePath treepath=tree.getSelectionPath();
等價,可互換。
我們將“我的電腦”改成“網上領居”:
我們再來看一個TreeModelEvent的例子,下面這個例子我們可以讓用戶自行增
加、刪除與修改節點:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.tree.*;
import com.incors.plaf.alloy.*;
import com.incors.plaf.alloy.themes.bedouin.*;
public class TreeDemo6 implements ActionListener,TreeModelListener{
JLabel label=null;
JTree tree=null;
DefaultTreeModel treeModel=null;
String nodeName=null;//原有節點名稱
public TreeDemo6(){
try {
AlloyLookAndFeel.setProperty("alloy.isLookAndFeelFrameDecoration", "true");
AlloyTheme theme = new BedouinTheme();
LookAndFeel alloyLnF = new AlloyLookAndFeel(theme);
UIManager.setLookAndFeel(alloyLnF);
} catch (UnsupportedLookAndFeelException ex) {
// You may handle the exception here
}
// this line needs to be implemented in order to make JWS work
properly
UIManager.getLookAndFeelDefaults().put("ClassLoader",
getClass().getClassLoader());
JFrame f=new JFrame("TreeDemo6");
Container contentPane=f.getContentPane();
DefaultMutableTreeNode root=new DefaultMutableTreeNode("資源管理器");
tree=new JTree(root);
tree.setEditable(true);
tree.addMouseListener(new MouseHandle());
treeModel=(DefaultTreeModel)tree.getModel();
treeModel.addTreeModelListener(this);
JScrollPane scrollPane=new JScrollPane();
scrollPane.setViewportView(tree);
JPanel panel=new JPanel();
JButton b=new JButton("新增節點");
b.addActionListener(this);
panel.add(b);
b=new JButton("刪除節點");
b.addActionListener(this);
panel.add(b);
b=new JButton("清除所有節點");
b.addActionListener(this);
panel.add(b);
label=new JLabel("Action");
contentPane.add(panel,BorderLayout.NORTH);
contentPane.add(scrollPane,BorderLayout.CENTER);
contentPane.add(label,BorderLayout.SOUTH);
f.pack();
f.setVisible(true);
f.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e){
System.exit(0);
}
});
}
//本方法運行新增、刪除、清除所有節點的程式代碼.
public void actionPerformed(ActionEvent ae){
if (ae.getActionCommand().equals("新增節點")){
DefaultMutableTreeNode parentNode=null;
DefaultMutableTreeNode newNode=new DefaultMutableTreeNode("新節點");
newNode.setAllowsChildren(true);
TreePath parentPath=tree.getSelectionPath();
//取得新節點的父節點
parentNode=(DefaultMutableTreeNode)(parentPath.getLastPathComponent());
//由DefaultTreeModel的insertNodeInto()方法增加新節點
treeModel.insertNodeInto(newNode,parentNode,parentNode.getChildCount());
//tree的scrollPathToVisible()方法在使Tree會自動展開文件夾以便顯
示所加入的新節點。若沒加這行則加入的新節點
//會被 包在文件夾中,你必須自行展開文件夾才看得到。
tree.scrollPathToVisible(new TreePath(newNode.getPath()));
label.setText("新增節點成功");
}
if (ae.getActionCommand().equals("刪除節點")){
TreePath treepath=tree.getSelectionPath();
if (treepath!=null){
//下面兩行取得選取節點的父節點.
DefaultMutableTreeNode
selectionNode=(DefaultMutableTreeNode)treepath.getLastPathComponent();
TreeNode parent=(TreeNode)selectionNode.getParent();
if (parent!=null) {
//由DefaultTreeModel的removeNodeFromParent()方法刪除節點,
包含它的子節點。
treeModel.removeNodeFromParent(selectionNode);
label.setText("刪除節點成功");
}
}
}
if (ae.getActionCommand().equals("清除所有節點")){
//下面一行,由DefaultTreeModel的getRoot()方法取得根節點.
DefaultMutableTreeNode
rootNode=(DefaultMutableTreeNode)treeModel.getRoot();
//下面一行刪除所有子節點.
rootNode.removeAllChildren();
//刪除完後務必運行DefaultTreeModel的reload()操作,整個Tree的節點
才會真正被刪除.
treeModel.reload();
label.setText("清除所有節點成功");
}
}
public void treeNodesChanged(TreeModelEvent e){
TreePath treePath=e.getTreePath();
DefaultMutableTreeNode
node=(DefaultMutableTreeNode)treePath.getLastPathComponent();
try{
int[] index=e.getChildIndices();
node=(DefaultMutableTreeNode)node.getChildAt(index[0]);
}catch(NullPointerException exc){}
label.setText(nodeName+"更改數據為:"+(String)node.getUserObject());
}
public void treeNodesInserted(TreeModelEvent e){
System.out.println("new node insered");
}
public void treeNodesRemoved(TreeModelEvent e){
System.out.println("node deleted");
}
public void treeStructureChanged(TreeModelEvent e){
System.out.println("Structrue changed");
}
public static void main(String[] args){
new TreeDemo6();
}
class MouseHandle extends MouseAdapter{
public void mousePressed(MouseEvent e){
try{
JTree tree=(JTree)e.getSource();
int rowLocation=tree.getRowForLocation(e.getX(),e.getY());
TreePath treepath=tree.getPathForRow(rowLocation);
TreeNode treenode=(TreeNode)treepath.getLastPathComponent();
nodeName=treenode.toString();
}catch(NullPointerException ne){}
}
}
}
10-7-2:處理TreeSelectionEvent事件:
當我們在JTree上點選任何一個節點,都會觸發TreeSelectionEvent事件,如
果我們要處理這樣的事件,必須實作
TreeSelectionListener界面,此界面只定義了一個方法,那就是valueChanged()
方法。
TreeSelectionEvent最常用在處理顯示節點的內容,例如你在文件圖標中點兩
下就可以看到文件的內容。在JTree中選擇節點
的方式共有3種,這3種情況跟選擇JList上的項目是一模一樣的,分別是:
DISCONTIGUOUS_TREE_SELECTION:可作單一選擇,連續點選擇(按住[Shift]
鍵),不連續選擇多個節點(按住[Ctrl]鍵),
這是java預設值.
CONTINUOUS_TREE_SELECTION:按住[Shift]鍵,可對某一連續的節點區間作
選取。
SINGLE_TREE_SELECTION:一次只能選一個節點。
你可以自行實作TreeSelectionModel製作作更複雜的選擇方式,但通常是沒有
必要的,因為java提供了預設的選擇模式類供我們
使用,那就是DefaultTreeSelectionModel,利用這個類我們可以很方便的設置上面
3種選擇模式。
下面這個範例,當用戶點選了一個文件名時,就會將文件的內容顯示出來。
TreeDemo7.java
import javax.swing.*;
import javax.swing.tree.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;
import com.incors.plaf.alloy.*;
import com.incors.plaf.alloy.themes.glass.*;
public class TreeDemo7 implements TreeSelectionListener
{
JEditorPane editorPane;
public TreeDemo7()
{
JFrame f = new JFrame("TreeDemo");
Container contentPane = f.getContentPane();
DefaultMutableTreeNode root = new DefaultMutableTreeNode("資源管
理器");
DefaultMutableTreeNode node = new
DefaultMutableTreeNode("TreeDemo1.java");
root.add(node);
node = new DefaultMutableTreeNode("TreeDemo2.java");
root.add(node);
node = new DefaultMutableTreeNode("TreeDemo3.java");
root.add(node);
node = new DefaultMutableTreeNode("TreeDemo4.java");
root.add(node);
JTree tree = new JTree(root);
//設置Tree的選擇模式為一次只能選擇一個節點
tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
//檢查是否有TreeSelectionEvent事件。
tree.addTreeSelectionListener(this);
//下面五行,JSplitPane中,左邊是放含有JTree的JScrollPane,右邊是
放JEditorPane.
JScrollPane scrollPane1 = new JScrollPane(tree);
editorPane = new JEditorPane();
JScrollPane scrollPane2 = new JScrollPane(editorPane);
JSplitPane splitPane = new JSplitPane(
JSplitPane.HORIZONTAL_SPLIT,true, scrollPane1, scrollPane2);
contentPane.add(splitPane);
f.pack();
f.setVisible(true);
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
//本方法實作valueChanged()方法
public void valueChanged(TreeSelectionEvent e)
{
JTree tree = (JTree) e.getSource();
//利用JTree的getLastSelectedPathComponent()方法取得目前選取的節點.
DefaultMutableTreeNode selectionNode =
(DefaultMutableTreeNode)tree.getLastSelectedPathComponent();
String nodeName = selectionNode.toString();
//判斷是否為樹葉節點,若是則顯示文件內容,若不是則不做任何事。
if (selectionNode.isLeaf())
{
/*取得文件的位置路徑,System.getProperty("user.dir")可以取得目
前工作的路徑,
*System.getProperty("file.separator")是取得文件分隔符,例如
在window環境的
*文件分陋符是"/",而Unix環境的文件分隔符剛好相反,是"/".利用
System.getProperty()
*方法你可以取得下列的信息:
java.version 顯示java版本
java.endor 顯示java製造商
java.endor.url 顯示java製造商URL
java.home 顯示java的安裝路徑
java.class.version 顯示java類版本
java.class.path 顯示java classpath
os.name 顯示操作系統名稱
os.arch 顯示操作系統結構,如x86
os.version 顯示操作系統版本
file.separator 取得文件分隔符
path.separator 取得路徑分隔符,如
Unix是以“:”表示
line.separator 取得換行符號,如
Unix是以"/n"表示
user.name 取得用戶名稱
user.home 取得用戶家目錄(home
directory),如Windows中Administrator的家目
錄為c:/Documents
and Settings/Administrator
user.dir 取得用戶目前的工作目錄.
*/
String filepath = "file:"+System.getProperty("user.dir") +
System.getProperty("file.separator") +
nodeName;
try {
//利用JEditorPane的setPage()方法將文件內容顯示在
editorPane中。若文件路徑錯誤,則會產生IOException.
editorPane.setPage(filepath);
} catch(IOException ex) {
System.out.println("找不到此文件");
}
}
}
public static void main(String[] args) {
SwingUtil.setLookAndFeel();
new TreeDemo7();
}
}
class SwingUtil{
public static final void setLookAndFeel() {
try{
Font font = new Font("JFrame", Font.PLAIN, 12);
Enumeration keys = UIManager.getLookAndFeelDefaults().keys();
while (keys.hasMoreElements()) {
Object key = keys.nextElement();
if (UIManager.get(key) instanceof Font) {
UIManager.put(key, font);
}
}
AlloyLookAndFeel.setProperty("alloy.isLookAndFeelFrameDecoration", "true");
AlloyTheme theme = new GlassTheme();
LookAndFeel alloyLnF = new AlloyLookAndFeel(theme);
JFrame.setDefaultLookAndFeelDecorated(true);
UIManager.setLookAndFeel(alloyLnF);
}catch(UnsupportedLookAndFeelException ex){
ex.printStackTrace();
}
}
}
10-8:JTree的其他操作:
我們在之前小節中曾說到Tree中的每一個節點都是一個TreeNode,並可利用
JTree的setEditable()方法設置節點是否可編輯,
若要在Tree中找尋節點的父節點或子節點,或判斷是否為樹節點,皆可由實作
TreeNode界面做到,但要編輯節點呢?java將編輯
節點的任務交給TreeCellEditor,TreeCellEditor本身是一個界面,裡面只定義了
getTreeCellEditor Component()方法,你可以實
作此方法使節點具有編輯的效果。不過你不用這麼辛苦去實作這個方法,java本身
提供了DefaultTreeCellEditor類來實作此方法
,亦提供了其他許多方法,例如取得節點內容(getCellEditorValue()) 、設置節
點字體(setFont())、決定節點是否可編輯
(isCellEditable())等等。除非你覺得DefaultTreeCellEditor所提供的功能不
夠,你才需要去實作TreeCellEditor界面。你可以利
用JTree的getCellEditor()方法取得DefaultTreeCellEditor對象。當我們編輯節
點時會觸發ChangeEvent事件,你可以實作
CellEditorListener界面來處理此事件,CellEditorListener界面包括兩個方法,
分別是editingStopped(ChangeEvent e)與
editingCanceled(ChangeEvent e).若你沒有實作TreeCellEditor界面,系統會以
預設的DefaultTreeCellEdtior類來處理掉這兩個
方法(你可以在DefaultTreeCellEditor中找到這兩個方法),因此你無須再編寫任
何的程式。
另外,JTree還有一種事件處理模式,那就是TreeExpansionEvent事件。要處
理這個事件你必須實作TreeExpansionListener
界面,此界面定義了兩個方法,分別是treeCollapsed(TreeExpansionEvent e)與
treeExpanded(TreeExpansionEvent e).當節點展
開時系統就會自動調用treeExpanded()方法,當節點合起來時,系統就會自動調用
treeCollapsed()方法。你可以在這兩個方法中編寫所要處理事情的程式碼。