JAVA SWING JTree全面了解

来源:互联网 发布:gb软件下载 编辑:程序博客网 时间:2024/05/21 19:35

基础了解

使用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  voidsetExpandedState(TreePath path, boolean state)
          设置此 JTree 的展开状态。
setExpandedState

protected void setExpandedState(TreePath path,                                boolean state)
设置此 JTree 的展开状态。如果 state 为 true,则 path 的所有父路径和路径都被标记为展开。如果 state 为 false,则 path 的所有父路径被标记为展开,但是path 本身被标记为折叠。

如果 TreeWillExpandListener 禁止它,则会失败。

 

 

 voidcollapsePath(TreePath path)
          确保指定路径标识的节点是折叠的,并且可查看。 voidcollapseRow(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在两个层面上存在差异:

  1. 在模型层上,CheckBoxTree的每个结点需要一个成员来保存其是否被选中,但是JTree的结点则不需要。
  2. 在视图层上,CheckBoxTree的每个结点比JTree的结点多显示一个复选框。
既然存在两个差异,那么只要我们把这两个差异部分通过自己的实现填补上,那么带复选框的树也就实现了。
现在开始解决第一个差异。为了解决第一个差异,需要定义一个新的结点类CheckBoxTreeNode,该类继承DefaultMutableTreeNode,并增加新的成员isSelected来表示该结点是否被选中。对于一颗CheckBoxTree,如果某一个结点被选中的话,其复选框会勾选上,并且使用CheckBoxTree的动机在于可以一次性地选中一颗子树。那么,在选中或取消一个结点时,其祖先结点和子孙结点应该做出某种变化。在此,我们应用如下递归规则:
  1. 如果某个结点被手动选中,那么它的所有子孙结点都应该被选中;如果选中该结点使其父节点的所有子结点都被选中,则选中其父结点。
  2. 如果某个结点被手动取消选中,那么它的所有子孙结点都应该被取消选中;如果该结点的父结点处于选中状态,则取消选中其父结点。
注意:上面的两条规则是递归规则,当某个结点发生变化,导致另外的结点发生变化时,另外的结点也会导致其他的结点发生变化。在上面两条规则中,强调手动,是因为手动选中或者手动取消选中一个结点,会导致其他结点发生非手动的选中或者取消选中,这种非手动导致的选中或者非取消选中则不适用于上述规则。

 

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);

             

       }

}