基础了解
使用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()方法。你可以在這兩個方法中編寫所要處理事情的程式碼。
JTree行滚动到可视图区域有专门的方法如下:
JTree.scrollPathToVisible(TreePath path) ---
scrollPathToVisible(TreePath path) 确保路径中所有的路径组件均展开(最后一个路径组件除外)并滚动,以便显示该路径标识的节点。
TreePath获得方法:
1.JTree.LeadSelectionPath()
2.JTreegetPathForLocation(int x, int y)
3.JTree.getPathForRow(int row)
4.DefaultMutableTreeNode.getPath() //返回从根到达此节点的路径 TreeNode[]
TreePath(Object[] path)//可构造出TreePath对象,传入参数TreeNode[].
树节点展开方法:这是一个保护方法,需要继承树才能重写
protected void | setExpandedState(TreePath path, boolean state) 设置此 JTree 的展开状态。 |
setExpandedState
protected void setExpandedState(TreePath path, boolean state)
-
设置此
JTree
的展开状态。 如果state
为 true,则path
的所有父路径和路径都被标记为展开。如果state
为 false,则path
的所有父路径被标记为展开,但是path
本身被标记为折叠。如果
TreeWillExpandListener
禁止它,则会失败。
void | collapsePath(TreePath path) 确保指定路径标识的节点是折叠的,并且可查看。 |
void | collapseRow(int row) 确保指定行中的节点是折叠的。 |
collapsePath
public void collapsePath(TreePath path)
- 确保指定路径标识的节点是折叠的,并且可查看。
-
-
参数:
-
path
- 标识节点的TreePath
-
collapseRow
public void collapseRow(int row)
-
确保指定行中的节点是折叠的。
如果
row
< 0 或 >=getRowCount
,则此方法无效。 -
-
-
参数:
-
row
- 指定一个显示行的整数,其中 0 表示显示的第一行
-
JTREE展开(不展开子节点)
方法1:protected void setExpandedState(TreePath path, boolean state)--树节点展开方法:这是一个保护方法,需要继承树才能重写.待测试.
public class JTreeEx extends JTree
{
private static final long serialVersionUID = 1L;
public JTreeEx(TreeModel newModel)
{
super(newModel);
}
@Override
public void setExpandedState(TreePath path, boolean state)
{
super.setExpandedState(path, state);
}
}
for (int i = 0; i < tree.getRowCount(); i++)
{
TreePath tp = tree.getPathForRow(i);
DefaultMutableTreeNode node = (DefaultMutableTreeNode) tp.getLastPathComponent();
if ("country1".equals(node.getUserObject().toString()))
{
// tree.setExpandedState(tp, false);--此方法不好使.后续有时间再测试,现用下面的方法实现.
tree.expandPath(tp);//先展开,
Enumeration<?> enums = node.children();
while (enums.hasMoreElements())
{
DefaultMutableTreeNode dtn = (DefaultMutableTreeNode) enums.nextElement();
TreePath tp1 = tp.pathByAddingChild(dtn);
tree.collapsePath(tp1);//后收缩
System.out.println("expande tp1:" + tp1);
}
}
}
}
方法2:先展开后收缩.
即public void collapsePath(TreePath path)--会展开此路径的所有父节点和子节点
public void expandPath(TreePath path)---收缩所有子节点.
Swing拓展JTree功能--仿QQ树
相信读者都清楚树在图形界面的重要性,但JAVA自带的JTree功能狭窄,运用起来非常的不方便,不灵活,比如在已经添加的节点中不能更改其文本信息和图片信息,而且所有的叶子节点的图片都是通过DefaultTreeCellRenderer的方法setLeafIcon()来实现的,所以全部的叶子节点的图片都是一样,这点在实际的软件开发中是一个致命的漏洞,比如腾讯QQ 的好友就是一棵树,总不可能全部好友的头像都一样吧,为此,笔者将通过自己的经验重写DefaultTreeCellRenderer类,让我们在开发中更灵活的运用树的组件。
要重写DefaultTreeCellRenderer,又要不失去它本身的功能,唯一的办法就是继承它,拓展它的方法,不仅要继承该类,我们还要继承DefaultMutableTreeNode类,使我们的节点更加丰富
下面笔者将创建类IconNodeRenderer继承类DefaultTreeCellRenderer,
创建类IconNode继承DefaultMutableTreeNode
为了更方便的使用我们自定义的树,笔者将这两个类封装在一个文件里,便于管理
package tree.imagetree;
import java.awt.Component;
import javax.swing.Icon;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
publicclass IconNodeRendererextends DefaultTreeCellRenderer//继承该类
{
//重写该方法
public Component getTreeCellRendererComponent(JTree tree, Object value,boolean sel,boolean expanded,
boolean leaf,int row,boolean hasFocus)
{
super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);//调用父类的该方法
Icon icon = ((IconNode) value).getIcon();//从节点读取图片
String txt = ((IconNode) value).getText();//从节点读取文本
setIcon(icon);//设置图片
setText(txt);//设置文本
returnthis;
}
}
// 定义节点类
class IconNodeextends DefaultMutableTreeNode
{
protected Iconicon;
protected Stringtxt;
//只包含文本的节点构造
public IconNode(String txt)
{
super();
this.txt = txt;
}
//包含文本和图片的节点构造
public IconNode(Icon icon, String txt)
{
super();
this.icon = icon;
this.txt = txt;
}
publicvoid setIcon(Icon icon)
{
this.icon = icon;
}
public Icon getIcon()
{
returnicon;
}
publicvoid setText(String txt)
{
this.txt = txt;
}
public String getText()
{
returntxt;
}
}
package tree.imagetree;
import java.awt.BorderLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreePath;
publicclass ImageTreeDemo
{
JFrame frame;
public ImageTreeDemo()
{
try
{
UIManager.setLookAndFeel("com.jtattoo.plaf.bernstein.BernsteinLookAndFeel");
SwingUtilities.updateComponentTreeUI(frame);
} catch (Exception e)
{
}
frame =new JFrame("树");
frame.setSize(150, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
IconNode root1 = new IconNode(new ImageIcon("3.png"),"高中同学");
IconNode root2 = new IconNode(new ImageIcon("3.png"),"初中同学");
root1.add(new IconNode(new ImageIcon("5.png"),"雅君"));
root1.add(new IconNode(new ImageIcon("1.png"),"伟旭"));
root1.add(new IconNode(new ImageIcon("2.png"),"宜群"));
root2.add(new IconNode(new ImageIcon("2.png"),"彬强"));
root2.add(new IconNode(new ImageIcon("1.png"),"小强"));
IconNode Root = new IconNode(null,null);//定义根节点
Root.add(root1);//定义二级节点
Root.add(root2);//定义二级节点
final JTree tree =new JTree(Root);//定义树
tree.setCellRenderer(new IconNodeRenderer());//设置单元格描述
tree.setEditable(false);//设置树是否可编辑
tree.setRootVisible(false);//设置树的根节点是否可视
tree.setToggleClickCount(1);//设置单击几次展开数节点
DefaultTreeCellRenderer cellRenderer = (DefaultTreeCellRenderer) tree.getCellRenderer();//获取该树的Renderer
cellRenderer.setClosedIcon(new ImageIcon("2.gif"));//关闭打开图标
cellRenderer.setOpenIcon(new ImageIcon("2.gif"));//设置展开图标
//测试事件
tree.addMouseListener(new MouseAdapter()
{
publicvoid mouseClicked(MouseEvent e)
{
if (e.getClickCount() == 2)//双击节点
{
TreePath path = tree.getSelectionPath();//获取选中节点路径
IconNode node = (IconNode) path.getLastPathComponent();//通过路径将指针指向该节点
if (node.isLeaf())//如果该节点是叶子节点
{
// DefaultTreeModel
// model=(DefaultTreeModel)tree.getModel();//获取该树的模型
// model.removeNodeFromParent(node);//从本树删除该节点
node.setIcon(new ImageIcon("3.png"));//修改该节点的图片
node.setText("双击");//修改该节点的文本
tree.repaint();//重绘更新树
System.out.println(node.getText());
} else
//不是叶子节点
{
}
}
}
});
JScrollPane sp = new JScrollPane(tree);
frame.getContentPane().add(sp, BorderLayout.CENTER);
frame.setVisible(true);
}
publicstaticvoid main(String[] args)
{
new ImageTreeDemo();
}
}
普通选择树实例
在使用Java Swing开发UI程序时,很有可能会遇到使用带复选框的树的需求,但是Java Swing并没有提供这个组件,因此如果你有这个需求,你就得自己动手实现带复选框的树。
CheckBoxTree与JTree在两个层面上存在差异:
- 在模型层上,CheckBoxTree的每个结点需要一个成员来保存其是否被选中,但是JTree的结点则不需要。
- 在视图层上,CheckBoxTree的每个结点比JTree的结点多显示一个复选框。
- 如果某个结点被手动选中,那么它的所有子孙结点都应该被选中;如果选中该结点使其父节点的所有子结点都被选中,则选中其父结点。
- 如果某个结点被手动取消选中,那么它的所有子孙结点都应该被取消选中;如果该结点的父结点处于选中状态,则取消选中其父结点。
package tree.checktree;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import javax.swing.JCheckBox;
import javax.swing.JPanel;
import javax.swing.JTree;
import javax.swing.UIManager;
import javax.swing.plaf.ColorUIResource;
import javax.swing.tree.TreeCellRenderer;
public class CheckBoxTreeCellRenderer extends JPanel implements TreeCellRenderer
{
private static final long serialVersionUID = 1L;
protected JCheckBox check;
protected CheckBoxTreeLabel label;
public CheckBoxTreeCellRenderer()
{
setLayout(null);
add(check = new JCheckBox());
add(label = new CheckBoxTreeLabel());
check.setBackground(UIManager.getColor("Tree.textBackground"));
label.setForeground(UIManager.getColor("Tree.textForeground"));
}
/**
* 返回的是一个<code>JPanel</code>对象,该对象中包含一个<code>JCheckBox</code>对象
* 和一个<code>JLabel</code>对象。并且根据每个结点是否被选中来决定<code>JCheckBox</code>
* 是否被选中。
*/
//@Override
public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean selected, boolean expanded, boolean leaf, int row,
boolean hasFocus)
{
String stringValue = tree.convertValueToText(value, selected, expanded, leaf, row, hasFocus);
setEnabled(tree.isEnabled());
check.setSelected(((CheckBoxTreeNode)value).isSelected());
label.setFont(tree.getFont());
label.setText(stringValue);
label.setSelected(selected);
label.setFocus(hasFocus);
if(leaf)
label.setIcon(UIManager.getIcon("Tree.leafIcon"));
else if(expanded)
label.setIcon(UIManager.getIcon("Tree.openIcon"));
else
label.setIcon(UIManager.getIcon("Tree.closedIcon"));
return this;
}
@Override
public Dimension getPreferredSize()
{
Dimension dCheck = check.getPreferredSize();
Dimension dLabel = label.getPreferredSize();
return new Dimension(dCheck.width + dLabel.width, dCheck.height < dLabel.height ? dLabel.height: dCheck.height);
}
@Override
public void doLayout()
{
Dimension dCheck = check.getPreferredSize();
Dimension dLabel = label.getPreferredSize();
int yCheck = 0;
int yLabel = 0;
if(dCheck.height < dLabel.height)
yCheck = (dLabel.height - dCheck.height) / 2;
else
yLabel = (dCheck.height - dLabel.height) / 2;
check.setLocation(0, yCheck);
check.setBounds(0, yCheck, dCheck.width, dCheck.height);
label.setLocation(dCheck.width, yLabel);
label.setBounds(dCheck.width, yLabel, dLabel.width, dLabel.height);
}
@Override
public void setBackground(Color color)
{
if(color instanceof ColorUIResource)
color = null;
super.setBackground(color);
}
}
package tree.checktree;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.Icon;
import javax.swing.JLabel;
import javax.swing.UIManager;
import javax.swing.plaf.ColorUIResource;
public class CheckBoxTreeLabel extends JLabel
{
private static final long serialVersionUID = 1L;
private boolean isSelected;
private boolean hasFocus;
public CheckBoxTreeLabel()
{
}
@Override
public void setBackground(Color color)
{
if(color instanceof ColorUIResource)
color = null;
super.setBackground(color);
}
@Override
public void paint(Graphics g)
{
String str;
if((str = getText()) != null)
{
if(0 < str.length())
{
if(isSelected)
g.setColor(UIManager.getColor("Tree.selectionBackground"));
else
g.setColor(UIManager.getColor("Tree.textBackground"));
Dimension d = getPreferredSize();
int imageOffset = 0;
Icon currentIcon = getIcon();
if(currentIcon != null)
imageOffset = currentIcon.getIconWidth() + Math.max(0, getIconTextGap() - 1);
g.fillRect(imageOffset, 0, d.width - 1 - imageOffset, d.height);
if(hasFocus)
{
g.setColor(UIManager.getColor("Tree.selectionBorderColor"));
g.drawRect(imageOffset, 0, d.width - 1 - imageOffset, d.height - 1);
}
}
}
super.paint(g);
}
@Override
public Dimension getPreferredSize()
{
Dimension retDimension = super.getPreferredSize();
if(retDimension != null)
retDimension = new Dimension(retDimension.width + 3, retDimension.height);
return retDimension;
}
public void setSelected(boolean isSelected)
{
this.isSelected = isSelected;
}
public void setFocus(boolean hasFocus)
{
this.hasFocus = hasFocus;
}
}
package tree.checktree;
import javax.swing.tree.DefaultMutableTreeNode;
publicclass CheckBoxTreeNodeextends DefaultMutableTreeNode
{
privatestaticfinallongserialVersionUID = 1L;
protectedbooleanisSelected;
public CheckBoxTreeNode()
{
this(null);
}
public CheckBoxTreeNode(Object userObject)
{
this(userObject,true,false);
}
public CheckBoxTreeNode(Object userObject,boolean allowsChildren,
boolean isSelected)
{
super(userObject, allowsChildren);
this.isSelected = isSelected;
}
publicboolean isSelected()
{
returnisSelected;
}
publicvoid setSelected(boolean _isSelected)
{
this.isSelected = _isSelected;
if (_isSelected)
{
//如果选中,则将其所有的子结点都选中
if (children !=null)
{
for (Object obj :children)
{
CheckBoxTreeNode node = (CheckBoxTreeNode) obj;
if (_isSelected != node.isSelected())
node.setSelected(_isSelected);
}
}
//向上检查,如果父结点的所有子结点都被选中,那么将父结点也选中
CheckBoxTreeNode pNode = (CheckBoxTreeNode)parent;
//开始检查pNode的所有子节点是否都被选中
if (pNode !=null)
{
int index = 0;
for (; index < pNode.children.size(); ++index)
{
CheckBoxTreeNode pChildNode = (CheckBoxTreeNode) pNode.children
.get(index);
if (!pChildNode.isSelected())
break;
}
/*
* 表明pNode所有子结点都已经选中,则选中父结点,该方法是一个递归方法,因此在此不需要进行迭代,因为
* 当选中父结点后,父结点本身会向上检查的。
*/
if (index == pNode.children.size())
{
if (pNode.isSelected() != _isSelected)
pNode.setSelected(_isSelected);
}
}
} else
{
/*
* 如果是取消父结点导致子结点取消,那么此时所有的子结点都应该是选择上的;
* 否则就是子结点取消导致父结点取消,然后父结点取消导致需要取消子结点,但是这时候是不需要取消子结点的。
*/
if (children !=null)
{
int index = 0;
for (; index <children.size(); ++index)
{
CheckBoxTreeNode childNode = (CheckBoxTreeNode)children
.get(index);
if (!childNode.isSelected())
break;
}
//从上向下取消的时候
if (index ==children.size())
{
for (int i = 0; i <children.size(); ++i)
{
CheckBoxTreeNode node = (CheckBoxTreeNode)children
.get(i);
if (node.isSelected() != _isSelected)
node.setSelected(_isSelected);
}
}
}
//向上取消,只要存在一个子节点不是选上的,那么父节点就不应该被选上。
CheckBoxTreeNode pNode = (CheckBoxTreeNode)parent;
if (pNode !=null && pNode.isSelected() != _isSelected)
pNode.setSelected(_isSelected);
}
}
}
package tree.checktree;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JTree;
import javax.swing.tree.TreePath;
import javax.swing.tree.DefaultTreeModel;
public class CheckBoxTreeNodeSelectionListener extends MouseAdapter
{
@Override
public void mouseClicked(MouseEvent event)
{
JTree tree = (JTree)event.getSource();
int x = event.getX();
int y = event.getY();
int row = tree.getRowForLocation(x, y);
TreePath path = tree.getPathForRow(row);
if(path != null)
{
CheckBoxTreeNode node = (CheckBoxTreeNode)path.getLastPathComponent();
if(node != null)
{
boolean isSelected = !node.isSelected();
node.setSelected(isSelected);
((DefaultTreeModel)tree.getModel()).nodeStructureChanged(node);
}
}
}
}
package tree.checktree;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.tree.DefaultTreeModel;
//http://www.jroller.com/resources/s/santhosh/TristateCheckBox.java
public class CheckBoxTreeDemo
{
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, UnsupportedLookAndFeelException
{
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
JFrame frame = new JFrame("CheckBoxTreeDemo");
frame.setBounds(200, 200, 400, 400);
JTree tree = new JTree();
CheckBoxTreeNode rootNode = new CheckBoxTreeNode("root");
CheckBoxTreeNode node1 = new CheckBoxTreeNode("node_1");
CheckBoxTreeNode node1_1 = new CheckBoxTreeNode("node_1_1");
CheckBoxTreeNode node1_2 = new CheckBoxTreeNode("node_1_2");
CheckBoxTreeNode node1_3 = new CheckBoxTreeNode("node_1_3");
node1.add(node1_1);
node1.add(node1_2);
node1.add(node1_3);
CheckBoxTreeNode node2 = new CheckBoxTreeNode("node_2");
CheckBoxTreeNode node2_1 = new CheckBoxTreeNode("node_2_1");
CheckBoxTreeNode node2_2 = new CheckBoxTreeNode("node_2_2");
node2.add(node2_1);
node2.add(node2_2);
rootNode.add(node1);
rootNode.add(node2);
DefaultTreeModel model = new DefaultTreeModel(rootNode);
tree.addMouseListener(new CheckBoxTreeNodeSelectionListener());
tree.setModel(model);
tree.setCellRenderer(new CheckBoxTreeCellRenderer());
JScrollPane scroll = new JScrollPane(tree);
scroll.setBounds(0, 0, 300, 320);
frame.getContentPane().add(scroll);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
三态选择树实例一
package tree.threetree;
import java.awt.BorderLayout;
import java.awt.Component;
import javax.swing.JPanel;
import javax.swing.JTree;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreePath;
public class CheckTreeCellRenderer extends JPanel implements TreeCellRenderer
{
private static final long serialVersionUID = 1L;
private CheckTreeSelectionModel selectionModel;
private TreeCellRenderer delegate;
private TristateCheckBox checkBox = new TristateCheckBox();
public CheckTreeCellRenderer(TreeCellRenderer delegate,
CheckTreeSelectionModel selectionModel)
{
this.delegate = delegate;
this.selectionModel = selectionModel;
setLayout(new BorderLayout());
setOpaque(false);
checkBox.setOpaque(false);
}
public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean selected, boolean expanded, boolean leaf, int row,
boolean hasFocus)
{
Component renderer = delegate.getTreeCellRendererComponent(tree, value,
selected, expanded, leaf, row, hasFocus);
TreePath path = tree.getPathForRow(row);
if (path != null)
{
if (selectionModel.isPathSelected(path, true))
checkBox.setState(TristateCheckBox.SELECTED); // Boolean.TRUE
else
checkBox
.setState(selectionModel.isPartiallySelected(path) ? TristateCheckBox.DONT_CARE
: TristateCheckBox.NOT_SELECTED);
// checkBox.setState(selectionModel.isPartiallySelected(path) ? null
// : Boolean.FALSE);
}
removeAll();
add(checkBox, BorderLayout.WEST);
add(renderer, BorderLayout.CENTER);
return this;
}
}
package tree.threetree;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JCheckBox;
import javax.swing.JTree;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.TreePath;
public class CheckTreeManager extends MouseAdapter implements
TreeSelectionListener
{
private CheckTreeSelectionModel selectionModel;
private JTree tree = new JTree();
int hotspot = new JCheckBox().getPreferredSize().width;
public CheckTreeManager(JTree tree)
{
this.tree = tree;
selectionModel = new CheckTreeSelectionModel(tree.getModel());
tree.setCellRenderer(new CheckTreeCellRenderer(tree.getCellRenderer(),
selectionModel));
tree.addMouseListener(this);
selectionModel.addTreeSelectionListener(this);
}
public void mouseClicked(MouseEvent me)
{
TreePath path = tree.getPathForLocation(me.getX(), me.getY());
if (path == null)
return;
if (me.getX() > tree.getPathBounds(path).x + hotspot)
return;
boolean selected = selectionModel.isPathSelected(path, true);
selectionModel.removeTreeSelectionListener(this);
try
{
if (selected)
selectionModel.removeSelectionPath(path);
else
selectionModel.addSelectionPath(path);
} finally
{
selectionModel.addTreeSelectionListener(this);
tree.treeDidChange();
}
}
public CheckTreeSelectionModel getSelectionModel()
{
return selectionModel;
}
public void valueChanged(TreeSelectionEvent e)
{
tree.treeDidChange();
}
}
package tree.threetree;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import javax.swing.tree.DefaultTreeSelectionModel;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
public class CheckTreeSelectionModel extends DefaultTreeSelectionModel
{
private static final long serialVersionUID = 1L;
private TreeModel model;
public CheckTreeSelectionModel(TreeModel model)
{
this.model = model;
setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
}
// tests whether there is any unselected node in the subtree of given path
public boolean isPartiallySelected(TreePath path)
{
if (isPathSelected(path, true))
return false;
TreePath[] selectionPaths = getSelectionPaths();
if (selectionPaths == null)
return false;
for (int j = 0; j < selectionPaths.length; j++)
{
if (isDescendant(selectionPaths[j], path))
return true;
}
return false;
}
// tells whether given path is selected.
// if dig is true, then a path is assumed to be selected, if
// one of its ancestor is selected.
public boolean isPathSelected(TreePath path, boolean dig)
{
if (!dig)
return super.isPathSelected(path);
while (path != null && !super.isPathSelected(path))
path = path.getParentPath();
return path != null;
}
// is path1 descendant of path2
private boolean isDescendant(TreePath path1, TreePath path2)
{
Object obj1[] = path1.getPath();
Object obj2[] = path2.getPath();
for (int i = 0; i < obj2.length; i++)
{
if (obj1[i] != obj2[i])
return false;
}
return true;
}
public void setSelectionPaths(TreePath[] pPaths)
{
throw new UnsupportedOperationException("not implemented yet!!!");
}
public void addSelectionPaths(TreePath[] paths)
{
// unselect all descendants of paths[]
for (int i = 0; i < paths.length; i++)
{
TreePath path = paths[i];
TreePath[] selectionPaths = getSelectionPaths();
if (selectionPaths == null)
break;
List<TreePath> toBeRemoved = new ArrayList<TreePath>();
for (int j = 0; j < selectionPaths.length; j++)
{
if (isDescendant(selectionPaths[j], path))
toBeRemoved.add(selectionPaths[j]);
}
super.removeSelectionPaths((TreePath[]) toBeRemoved
.toArray(new TreePath[0]));
}
// if all siblings are selected then unselect them and select parent
// recursively
// otherwize just select that path.
for (int i = 0; i < paths.length; i++)
{
TreePath path = paths[i];
TreePath temp = null;
while (areSiblingsSelected(path))
{
temp = path;
if (path.getParentPath() == null)
break;
path = path.getParentPath();
}
if (temp != null)
{
if (temp.getParentPath() != null)
addSelectionPath(temp.getParentPath());
else
{
if (!isSelectionEmpty())
removeSelectionPaths(getSelectionPaths());
super.addSelectionPaths(new TreePath[]
{ temp });
}
} else
super.addSelectionPaths(new TreePath[]
{ path });
}
}
// tells whether all siblings of given path are selected.
private boolean areSiblingsSelected(TreePath path)
{
TreePath parent = path.getParentPath();
if (parent == null)
return true;
Object node = path.getLastPathComponent();
Object parentNode = parent.getLastPathComponent();
int childCount = model.getChildCount(parentNode);
for (int i = 0; i < childCount; i++)
{
Object childNode = model.getChild(parentNode, i);
if (childNode == node)
continue;
if (!isPathSelected(parent.pathByAddingChild(childNode)))
return false;
}
return true;
}
public void removeSelectionPaths(TreePath[] paths)
{
for (int i = 0; i < paths.length; i++)
{
TreePath path = paths[i];
if (path.getPathCount() == 1)
super.removeSelectionPaths(new TreePath[]
{ path });
else
toggleRemoveSelection(path);
}
}
// if any ancestor node of given path is selected then unselect it
// and selection all its descendants except given path and descendants.
// otherwise just unselect the given path
private void toggleRemoveSelection(TreePath path)
{
Stack<TreePath> stack = new Stack<TreePath>();
TreePath parent = path.getParentPath();
while (parent != null && !isPathSelected(parent))
{
stack.push(parent);
parent = parent.getParentPath();
}
if (parent != null)
stack.push(parent);
else
{
super.removeSelectionPaths(new TreePath[]
{ path });
return;
}
while (!stack.isEmpty())
{
TreePath temp = (TreePath) stack.pop();
TreePath peekPath = stack.isEmpty() ? path : (TreePath) stack
.peek();
Object node = temp.getLastPathComponent();
Object peekNode = peekPath.getLastPathComponent();
int childCount = model.getChildCount(node);
for (int i = 0; i < childCount; i++)
{
Object childNode = model.getChild(node, i);
if (childNode != peekNode)
super.addSelectionPaths(new TreePath[]
{ temp.pathByAddingChild(childNode) });
}
}
super.removeSelectionPaths(new TreePath[]
{ parent });
}
}
package tree.threetree;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.ButtonGroup;
import javax.swing.ButtonModel;
import javax.swing.Icon;
import javax.swing.JCheckBox;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.ActionMapUIResource;
public class TristateCheckBox extends JCheckBox
{
private static final long serialVersionUID = 1L;
/** This is a type-safe enumerated type */
public static class State
{
private State()
{
}
}
public static final State NOT_SELECTED = new State();
public static final State SELECTED = new State();
public static final State DONT_CARE = new State();
private final TristateDecorator model;
@SuppressWarnings("serial")
public TristateCheckBox(String text, Icon icon, State initial)
{
super(text, icon);
// Add a listener for when the mouse is pressed
super.addMouseListener(new MouseAdapter()
{
public void mousePressed(MouseEvent e)
{
grabFocus();
model.nextState();
}
});
// Reset the keyboard action map
ActionMap map = new ActionMapUIResource();
map.put("pressed", new AbstractAction()
{
public void actionPerformed(ActionEvent e)
{
grabFocus();
model.nextState();
}
});
map.put("released", null);
SwingUtilities.replaceUIActionMap(this, map);
// set the model to the adapted model
model = new TristateDecorator(getModel());
setModel(model);
setState(initial);
}
public TristateCheckBox(String text, State initial)
{
this(text, null, initial);
}
public TristateCheckBox(String text)
{
this(text, DONT_CARE);
}
public TristateCheckBox()
{
this(null);
}
/** No one may add mouse listeners, not even Swing! */
public void addMouseListener(MouseListener l)
{
}
/**
* Set the new state to either SELECTED, NOT_SELECTED or DONT_CARE. If state ==
* null, it is treated as DONT_CARE.
*/
public void setState(State state)
{
model.setState(state);
}
/**
* Return the current state, which is determined by the selection status of
* the model.
*/
public State getState()
{
return model.getState();
}
public void setSelected(boolean b)
{
if (b)
{
setState(SELECTED);
} else
{
setState(NOT_SELECTED);
}
}
/**
* Exactly which Design Pattern is this? Is it an Adapter, a Proxy or a
* Decorator? In this case, my vote lies with the Decorator, because we are
* extending functionality and "decorating" the original model with a more
* powerful model.
*/
private class TristateDecorator implements ButtonModel
{
private final ButtonModel other;
private TristateDecorator(ButtonModel other)
{
this.other = other;
}
private void setState(State state)
{
if (state == NOT_SELECTED)
{
other.setArmed(false);
setPressed(false);
setSelected(false);
} else if (state == SELECTED)
{
other.setArmed(false);
setPressed(false);
setSelected(true);
} else
{ // either "null" or DONT_CARE
other.setArmed(true);
setPressed(true);
setSelected(true);
}
}
/**
* The current state is embedded in the selection / armed state of the
* model.
*
* We return the SELECTED state when the checkbox is selected but not
* armed, DONT_CARE state when the checkbox is selected and armed (grey)
* and NOT_SELECTED when the checkbox is deselected.
*/
private State getState()
{
if (isSelected() && !isArmed())
{
// normal black tick
return SELECTED;
} else if (isSelected() && isArmed())
{
// don't care grey tick
return DONT_CARE;
} else
{
// normal deselected
return NOT_SELECTED;
}
}
/** We rotate between NOT_SELECTED, SELECTED and DONT_CARE. */
private void nextState()
{
State current = getState();
if (current == NOT_SELECTED)
{
setState(SELECTED);
} else if (current == SELECTED)
{
setState(DONT_CARE);
} else if (current == DONT_CARE)
{
setState(NOT_SELECTED);
}
}
/** Filter: No one may change the armed status except us. */
public void setArmed(boolean b)
{
}
/**
* We disable focusing on the component when it is not enabled.
*/
public void setEnabled(boolean b)
{
setFocusable(b);
other.setEnabled(b);
}
/**
* All these methods simply delegate to the "other" model that is being
* decorated.
*/
public boolean isArmed()
{
return other.isArmed();
}
public boolean isSelected()
{
return other.isSelected();
}
public boolean isEnabled()
{
return other.isEnabled();
}
public boolean isPressed()
{
return other.isPressed();
}
public boolean isRollover()
{
return other.isRollover();
}
public void setSelected(boolean b)
{
other.setSelected(b);
}
public void setPressed(boolean b)
{
other.setPressed(b);
}
public void setRollover(boolean b)
{
other.setRollover(b);
}
public void setMnemonic(int key)
{
other.setMnemonic(key);
}
public int getMnemonic()
{
return other.getMnemonic();
}
public void setActionCommand(String s)
{
other.setActionCommand(s);
}
public String getActionCommand()
{
return other.getActionCommand();
}
public void setGroup(ButtonGroup group)
{
other.setGroup(group);
}
public void addActionListener(ActionListener l)
{
other.addActionListener(l);
}
public void removeActionListener(ActionListener l)
{
other.removeActionListener(l);
}
public void addItemListener(ItemListener l)
{
other.addItemListener(l);
}
public void removeItemListener(ItemListener l)
{
other.removeItemListener(l);
}
public void addChangeListener(ChangeListener l)
{
other.addChangeListener(l);
}
public void removeChangeListener(ChangeListener l)
{
other.removeChangeListener(l);
}
public Object[] getSelectedObjects()
{
return other.getSelectedObjects();
}
}
}
package tree.threetree;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
public class TristateTreeDemo
{
public static void main(String args[]) throws ClassNotFoundException, InstantiationException, IllegalAccessException, UnsupportedLookAndFeelException
{
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
DefaultMutableTreeNode world = new DefaultMutableTreeNode("World");
DefaultMutableTreeNode country1 = new DefaultMutableTreeNode("country1");
DefaultMutableTreeNode city1 = new DefaultMutableTreeNode("city1");
DefaultMutableTreeNode city1_1 = new DefaultMutableTreeNode("city1");
country1.add(city1);
country1.add(city1_1);
world.add(country1);
DefaultMutableTreeNode country2 = new DefaultMutableTreeNode("country2");
DefaultMutableTreeNode city2 = new DefaultMutableTreeNode("city2");
country2.add(city2);
world.add(country2);
DefaultMutableTreeNode country3 = new DefaultMutableTreeNode("country3");
DefaultMutableTreeNode city3 = new DefaultMutableTreeNode("city3");
country3.add(city3);
world.add(country3);
DefaultMutableTreeNode country4 = new DefaultMutableTreeNode("country4");
DefaultMutableTreeNode city4 = new DefaultMutableTreeNode("city4");
country4.add(city4);
world.add(country4);
TreeNode root = world;
DefaultTreeModel model = new DefaultTreeModel(root);
JTree tree = new JTree(model);
DefaultTreeCellRenderer myRenderer = new DefaultTreeCellRenderer();
tree.setCellRenderer(myRenderer);
CheckTreeManager checkTreeManager = new CheckTreeManager(tree);
//下面设置默认路径选中
TreeNode[] nodes = model.getPathToRoot(city1);
TreePath hh1 = new TreePath(nodes);
nodes = model.getPathToRoot(city2);
TreePath hh2 = new TreePath(nodes);
nodes = model.getPathToRoot(city3);
TreePath hh3 = new TreePath(nodes);
TreeSelectionModel selModel = checkTreeManager.getSelectionModel();
selModel.addSelectionPaths(new TreePath[]
{ hh1, hh2, hh3 });
//展开所有节点
for (int i = 0; i < tree.getRowCount(); i++)
{
tree.expandRow(i);
}
JScrollPane TreePanel = new JScrollPane(tree);
JFrame frame = new JFrame("Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(TreePanel);
frame.setBounds(200, 200, 200, 200);
frame.setVisible(true);
}
}
三态选择树实例二
package tree.threetree;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.ButtonGroup;
import javax.swing.ButtonModel;
import javax.swing.Icon;
import javax.swing.JCheckBox;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.ActionMapUIResource;
public class TristateCheckBox extends JCheckBox
{
private static final long serialVersionUID = 1L;
/** This is a type-safe enumerated type */
public static class State
{
private State()
{
}
}
public static final State NOT_SELECTED = new State();
public static final State SELECTED = new State();
public static final State DONT_CARE = new State();
private final TristateDecorator model;
@SuppressWarnings("serial")
public TristateCheckBox(String text, Icon icon, State initial)
{
super(text, icon);
// Add a listener for when the mouse is pressed
super.addMouseListener(new MouseAdapter()
{
public void mousePressed(MouseEvent e)
{
grabFocus();
model.nextState();
}
});
// Reset the keyboard action map
ActionMap map = new ActionMapUIResource();
map.put("pressed", new AbstractAction()
{
public void actionPerformed(ActionEvent e)
{
grabFocus();
model.nextState();
}
});
map.put("released", null);
SwingUtilities.replaceUIActionMap(this, map);
// set the model to the adapted model
model = new TristateDecorator(getModel());
setModel(model);
setState(initial);
}
public TristateCheckBox(String text, State initial)
{
this(text, null, initial);
}
public TristateCheckBox(String text)
{
this(text, DONT_CARE);
}
public TristateCheckBox()
{
this(null);
}
/** No one may add mouse listeners, not even Swing! */
public void addMouseListener(MouseListener l)
{
}
/**
* Set the new state to either SELECTED, NOT_SELECTED or DONT_CARE. If state ==
* null, it is treated as DONT_CARE.
*/
public void setState(State state)
{
model.setState(state);
}
/**
* Return the current state, which is determined by the selection status of
* the model.
*/
public State getState()
{
return model.getState();
}
public void setSelected(boolean b)
{
if (b)
{
setState(SELECTED);
} else
{
setState(NOT_SELECTED);
}
}
/**
* Exactly which Design Pattern is this? Is it an Adapter, a Proxy or a
* Decorator? In this case, my vote lies with the Decorator, because we are
* extending functionality and "decorating" the original model with a more
* powerful model.
*/
private class TristateDecorator implements ButtonModel
{
private final ButtonModel other;
private TristateDecorator(ButtonModel other)
{
this.other = other;
}
private void setState(State state)
{
if (state == NOT_SELECTED)
{
other.setArmed(false);
setPressed(false);
setSelected(false);
} else if (state == SELECTED)
{
other.setArmed(false);
setPressed(false);
setSelected(true);
} else
{ // either "null" or DONT_CARE
other.setArmed(true);
setPressed(true);
setSelected(true);
}
}
/**
* The current state is embedded in the selection / armed state of the
* model.
*
* We return the SELECTED state when the checkbox is selected but not
* armed, DONT_CARE state when the checkbox is selected and armed (grey)
* and NOT_SELECTED when the checkbox is deselected.
*/
private State getState()
{
if (isSelected() && !isArmed())
{
// normal black tick
return SELECTED;
} else if (isSelected() && isArmed())
{
// don't care grey tick
return DONT_CARE;
} else
{
// normal deselected
return NOT_SELECTED;
}
}
/** We rotate between NOT_SELECTED, SELECTED and DONT_CARE. */
private void nextState()
{
State current = getState();
if (current == NOT_SELECTED)
{
setState(SELECTED);
} else if (current == SELECTED)
{
setState(DONT_CARE);
} else if (current == DONT_CARE)
{
setState(NOT_SELECTED);
}
}
/** Filter: No one may change the armed status except us. */
public void setArmed(boolean b)
{
}
/**
* We disable focusing on the component when it is not enabled.
*/
public void setEnabled(boolean b)
{
setFocusable(b);
other.setEnabled(b);
}
/**
* All these methods simply delegate to the "other" model that is being
* decorated.
*/
public boolean isArmed()
{
return other.isArmed();
}
public boolean isSelected()
{
return other.isSelected();
}
public boolean isEnabled()
{
return other.isEnabled();
}
public boolean isPressed()
{
return other.isPressed();
}
public boolean isRollover()
{
return other.isRollover();
}
public void setSelected(boolean b)
{
other.setSelected(b);
}
public void setPressed(boolean b)
{
other.setPressed(b);
}
public void setRollover(boolean b)
{
other.setRollover(b);
}
public void setMnemonic(int key)
{
other.setMnemonic(key);
}
public int getMnemonic()
{
return other.getMnemonic();
}
public void setActionCommand(String s)
{
other.setActionCommand(s);
}
public String getActionCommand()
{
return other.getActionCommand();
}
public void setGroup(ButtonGroup group)
{
other.setGroup(group);
}
public void addActionListener(ActionListener l)
{
other.addActionListener(l);
}
public void removeActionListener(ActionListener l)
{
other.removeActionListener(l);
}
public void addItemListener(ItemListener l)
{
other.addItemListener(l);
}
public void removeItemListener(ItemListener l)
{
other.removeItemListener(l);
}
public void addChangeListener(ChangeListener l)
{
other.addChangeListener(l);
}
public void removeChangeListener(ChangeListener l)
{
other.removeChangeListener(l);
}
public Object[] getSelectedObjects()
{
return other.getSelectedObjects();
}
}
}
package tree.threetree1;
import java.awt.Component;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import javax.swing.JTree;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import tree.threetree.TristateCheckBox;
public class ThreeCheckboxTree
{
// 用一个HashMap来保存每个节点的选择状态
private HashMap<TreeNode, TristateCheckBox.State> hm = new HashMap<TreeNode, TristateCheckBox.State>();
public ThreeCheckboxTree(JTree jTree)
{
DefaultMutableTreeNode root = (DefaultMutableTreeNode) jTree.getModel().getRoot();
for (Enumeration<?> em = root.depthFirstEnumeration(); em.hasMoreElements();)
{
hm.put((DefaultMutableTreeNode) em.nextElement(), TristateCheckBox.NOT_SELECTED);
}
jTree.setCellRenderer(new MyTreeWithCheckboxRenderer());
jTree.addTreeSelectionListener(new MyTreeWithCheckboxSelectionListener());
}
/** 返回所有选中的路径 */
public TreePath[] getSelectionPaths()
{
Iterator<?> it = hm.keySet().iterator();
ArrayList<TreePath> al = new ArrayList<TreePath>();
while (it.hasNext())
{
DefaultMutableTreeNode o = (DefaultMutableTreeNode) it.next();
if (o.isLeaf() && ((TristateCheckBox.State) hm.get(o)).equals(TristateCheckBox.SELECTED))
{
al.add(new TreePath(o.getPath()));
}
}
return (TreePath[]) al.toArray(new TreePath[0]);
}
class MyTreeWithCheckboxSelectionListener implements TreeSelectionListener
{
public void valueChanged(TreeSelectionEvent e)
{
JTree jTree = (JTree) (e.getSource());
DefaultMutableTreeNode node = (DefaultMutableTreeNode) jTree.getLastSelectedPathComponent();
if (node == null)
{
return;
}
//修改当前节点的状态
if (((TristateCheckBox.State) hm.get(node)).equals(TristateCheckBox.SELECTED))
{
hm.put(node, TristateCheckBox.NOT_SELECTED);
} else
{
hm.put(node, TristateCheckBox.SELECTED);
}
updateAllParentNodes(node);
updateAllChildNodes(node);
jTree.setSelectionPath(null);
jTree.repaint();
}
/** 用递归方法修改从当前节点到根节点的所有节点的状态 */
public void updateAllParentNodes(DefaultMutableTreeNode node)
{
TristateCheckBox.State status = (TristateCheckBox.State) hm.get(node);
if (node.isRoot())
{
return;
}
hm.put(node.getParent(), status);
for (Enumeration<?> em = node.getParent().children(); em.hasMoreElements();)
{
DefaultMutableTreeNode tn = (DefaultMutableTreeNode) em.nextElement();
if (!((TristateCheckBox.State) hm.get(tn)).equals(status))
{
hm.put(node.getParent(), TristateCheckBox.DONT_CARE);
break;
}
}
updateAllParentNodes((DefaultMutableTreeNode) node.getParent());
}
/** 用递归方法修改以当前节点为根的子树的所有节点的状态 */
public void updateAllChildNodes(DefaultMutableTreeNode node)
{
TristateCheckBox.State status = (TristateCheckBox.State) hm.get(node);
for (Enumeration<?> em = node.depthFirstEnumeration(); em.hasMoreElements();)
{
DefaultMutableTreeNode tn = (DefaultMutableTreeNode) em.nextElement();
hm.put(tn, status);
}
}
}
class MyTreeWithCheckboxRenderer extends DefaultTreeCellRenderer
{
private static final long serialVersionUID = 1L;
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded,
boolean leaf, int row, boolean hasFocus)
{
TristateCheckBox checkBox = new TristateCheckBox(value.toString());
checkBox.setOpaque(false);
DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
checkBox.setState(((TristateCheckBox.State) hm.get(node)));
return checkBox;
}
}
}
package tree.threetree1;
import java.awt.BorderLayout;
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class ThreeCheckboxTreeDemo extends JPanel
{
private static final long serialVersionUID = 1L;
public ThreeCheckboxTreeDemo()
{
setLayout(new BorderLayout());
JTree tree = new JTree();
new ThreeCheckboxTree(tree);
JScrollPane jsp = new JScrollPane(tree);
jsp.setPreferredSize(new Dimension(500, 400));
add(jsp);
}
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, UnsupportedLookAndFeelException
{
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
ThreeCheckboxTreeDemo panel = new ThreeCheckboxTreeDemo();
JFrame frame = new JFrame("ThreeCheckboxTreeDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel.setOpaque(true);
frame.setContentPane(panel);
frame.pack();
frame.setVisible(true);
}
}