Jtree不同节点使用不同图片实现

在总结了数人的博客和自己的探索之下,终于实现,其中主要就是TreeCellRenderer这个接口的实现,

下面代码

用的Jcreator,需要在项目文件了加入image文件夹和图片文件。

Test.java


 
package myprojects.test;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.tree.*;
import javax.swing.event.*;


public class Test extends JFrame implements TreeSelectionListener{
  JButton addB, deleteB,addBS,addT;
  JTree tree;
  DefaultTreeModel treeModel;
  DefaultMutableTreeNode leadSelection;
  ImageIcon test;
  JLabel label;

    
public Test() {
    
super("Tree Event Demo");
    setSize(
600,400);
    setDefaultCloseOperation(EXIT_ON_CLOSE);

            

    DefaultMutableTreeNode root 
= new DefaultMutableTreeNode("Root");
    treeModel 
= new DefaultTreeModel(root);
    tree 
= new JTree(treeModel);
    tree.setExpandsSelectedPaths(
true);
    tree.setEditable(
true);
    getContentPane( ).add(
new JScrollPane(tree), BorderLayout.CENTER);
    tree.addTreeSelectionListener(
this);
    
    MyTreeCellRenderer myCellRenderer 
= new MyTreeCellRenderer();  
    
//设置叶子节点的图标   
    tree.setCellRenderer(myCellRenderer);
    
        
//按钮
    addB = new JButton("Add a Child node");
    addBS
=new JButton("Add a Sibling node");
    deleteB 
= new JButton("Delete a node");
    addT
=new JButton("Test");
    
    JPanel buttonP 
= new JPanel( );
    buttonP.add(addB);
    buttonP.add(addBS);
    buttonP.add(deleteB);
    buttonP.add(addT);
    getContentPane( ).add(buttonP, BorderLayout.SOUTH);

    addB.addActionListener(
new ActionListener( ) {
        
public void actionPerformed(ActionEvent ae) {
          String nodeName 
= JOptionPane.showInputDialog("New node name:");
          
if (leadSelection != null&&nodeName!=null) {
            leadSelection.add(
new DefaultMutableTreeNode(nodeName));
            ((DefaultTreeModel)tree.getModel( )).reload(leadSelection);
          }
          
else {
            JOptionPane.showMessageDialog(Test.
this"Error! Please select a node and input the nodename");
          }
        }
      });

    addBS.addActionListener(
new ActionListener(){
        
public void actionPerformed(ActionEvent ae) {
          String nodeName 
= JOptionPane.showInputDialog("New node name:");
          
if (leadSelection != null&&leadSelection.getParent()!=null&&nodeName!=null) {
            ((DefaultMutableTreeNode)leadSelection.getParent()).add(
new DefaultMutableTreeNode(nodeName));//该步骤改为先寻找parent然后加node
            ((DefaultTreeModel)tree.getModel( )).reload(leadSelection.getParent());
          }
          
else {
            JOptionPane.showMessageDialog(Test.
this"Error! Please select a node, input the nodename and insure the node is not the root");
          }
        }
    });
    
    deleteB.addActionListener(
new ActionListener( ) {
        
public void actionPerformed(ActionEvent ae) {
          
if (leadSelection != null) {
            DefaultMutableTreeNode parent 
= 
              (DefaultMutableTreeNode) leadSelection.getParent( );
            
if (parent == null) {
              JOptionPane.showMessageDialog(Test.
this"Can't delete root");
            }
            
else {
              parent.remove(leadSelection);
              leadSelection 
= null;
              ((DefaultTreeModel)tree.getModel( )).reload(parent);
            }
          }
          
else {
            JOptionPane.showMessageDialog(Test.
this"No Selection...");
          }
        }
      }); 
      
      addT.addActionListener(
          
new ActionListener( ) {
        
public void actionPerformed(ActionEvent ae) {
            String nodeName 
= JOptionPane.showInputDialog("New node name:");
            test
=new ImageIcon("../image/class.PNG");
            label
=new JLabel();
            label.setIcon(test);
            label.setText(nodeName);
          
if (leadSelection != null&&nodeName!=null) {
            leadSelection.add(
new DefaultMutableTreeNode(label));
            ((DefaultTreeModel)tree.getModel( )).reload(leadSelection);
          }
          
else {
            JOptionPane.showMessageDialog(Test.
this"Error! Please select a node and input the nodename");
          }
            }
     }); 
    }
    
    
    
    
      
public void valueChanged(TreeSelectionEvent e) {
    TreePath leadPath 
= e.getNewLeadSelectionPath( );
    
if (leadPath != null) {
      leadSelection 
= (DefaultMutableTreeNode)leadPath.getLastPathComponent( );
    }
  }


    
public static void main(String args[]) {
        
//System.out.println("Starting Test...");
        
//Test mainFrame = new Test();
        
//mainFrame.setSize(400, 400);
        
//mainFrame.setTitle("Test");
        
//mainFrame.setVisible(true);
            Test te = new Test( );
            te.setVisible(
true);

    }
}

 

Ok,另外MyTreeCellRenderer类的代码如下MyTreeCellRenderer.java
package myprojects.test;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.tree.*;
import javax.swing.event.*;
class MyTreeCellRenderer extends DefaultTreeCellRenderer {
    
public Component getTreeCellRendererComponent(JTree tree, Object value,boolean selected, boolean expanded,boolean leaf, int row, boolean hasFocus) {
       
   DefaultMutableTreeNode node 
= (DefaultMutableTreeNode)value;
   Object obj 
= node.getUserObject();
   
if(obj instanceof JLabel) {
        JLabel label 
= (JLabel)obj;
        DefaultTreeCellRenderer tempCellRenderer 
= new DefaultTreeCellRenderer();
        tempCellRenderer.setLeafIcon(label.getIcon());
        
return  tempCellRenderer.getTreeCellRendererComponent(tree,label.getText(),selected,expanded,true,row,hasFocus);
   }
   
return super.getTreeCellRendererComponent(tree,value,
selected,expanded,leaf,row,hasFocus);
   }
}

 

整个类是gool实现的,我写了个Test用了一下而已,不过作为一个整体,理解起来比较容易。

http://blog.sina.com.cn/s/blog_47e3d38d01000716.html

 

JTree及JTable的一些“剖析”
1.JTree的显示
最近因为要使用JTree及JTable做一些图片的显示及操作,因为以前都是肤浅的用过,所以并为深入。现在遇到的问题如在JTree里面如何单独显示每个cell的图片,比如在JTable里面如何画出别的控件等!
在网寻求帮助,但效果并不是很好,所以自行研究了一翻,虽然并未象那些资深人士一般做出精辟的真正的剖析,但我相信我的“一些剖析”还是能帮助到一些正在,或者正要使用这两大swing控件的人。
首先来说下JTree,单就显示父节点,子节点的图片是比较简单的,我们用DefaultTreeCellRenderer这个类就能实现。调用setOpenIcon(), setCloseIcon(), setLeafIcon()三个方法,分别实现父节点打开的图片,父节点关闭的图片,以及叶节点的显示图片。然后在相应需要显示的JTree里面调用setCellRenderer(TreeCellRenderer x)即可。至于别的字体,背景色等都在DefaultTreeCellRenderer里面相应的set方面里面,大家可通过JDK自行寻找。
这上面说的方法是极为简单的实现图片的方法,一般也就足够了,但是如果你想在节点里面实现自己的控件或者每个cell都需要拥有自己图片如何操作呢?使用DefaultTreeCellRenderer是不能满足我们的要求的,至少我是没找到简便的方法!因为使用JTree的setCellRenderer(TreeCellRenderer x) 方法后,是使所有cell都使用实现了TreeCellRenderer接口的类,这里我们用的是DefaultTreeCellRenderer来进行显示的.
那么我们先来看看DefaultTreeCellRenderer是如何实现TreeCellRenderer这个接口的:
    public Component getTreeCellRendererComponent(JTree tree, Object value,
        boolean sel,
        boolean expanded,
        boolean leaf, int row,
        boolean hasFocus) {
  String  stringValue = tree.convertValueToText(value, sel,
       expanded, leaf, row, hasFocus);

       this.tree = tree;
  this.hasFocus = hasFocus;
  setText(stringValue);
  if(sel)
      setForeground(getTextSelectionColor());
  else
      setForeground(getTextNonSelectionColor());
  // There needs to be a way to specify disabled icons.
  if (!tree.isEnabled()) {
      setEnabled(false);
      if (leaf) {
   setDisabledIcon(getLeafIcon());
      } else if (expanded) {
   setDisabledIcon(getOpenIcon());
      } else {
   setDisabledIcon(getClosedIcon());
      }
  }
  else {
      setEnabled(true);
      if (leaf) {
    setIcon(getLeafIcon());
      } else if (expanded) {
    setIcon(getOpenIcon());
      } else {
    setIcon(getClosedIcon());
      }
  }
        setComponentOrientation(tree.getComponentOrientation());
    
  selected = sel;

  return this;
    }
以上的橙色部分就是DefaultTreeCellRenderer为节点设置图片的地方,这个类本就是继承JLabel并且实现的TreeCellRenderer接口,所以以上蓝色部分是把传来的value加到Label的文字部分,并且return的是this,也就是标签了。
至于getLeafIcon(),getClosedIcon(),getOpenIcon(),对应得到的值就是我们调用对应的set方法给的值.
了解到这些你是否有想法了?对了,那就是我们自己实现TreeCellRenderer接口。那么我们就能显示自己需要的图片及控件了。但是前提还有个问题需要我们了解,那就是我们的节点—DefaultMutableTreeNode;构造一个节点时可以传任意的一个类,因为构造函数是用Object类来接收的:public DefaultMutableTreeNode(Object userObject) 。所以我们可以直接传入需要的东西,比如我们需要图片加文字,就直接传入Label即可,然后让我们自己实现了TreeCellRenderer的类实现就行了。而不必担心象DefaultTreeCellRenderer一样,标签的Text来源于Node构造时传入的对象的.toString(),而图片来源于事先set的ImageIcon. 这样,我们的JTree就可以多样化了,想要什么东西就传入什么东西,而不必担心他显示不出来。
而我们即将实现的接口public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) ;
着色的形参value是个节点对象,那么这里就需要用到getUserObject()方法来获取我们构造DefaultMutableTreeNode时传入的对象,运用RTTI来识别类型,如果是我们希望的类型就做出处理,否则就象DefaultTreeCellRenderer一样,用默认的ImageIcon加上value.toString来显示.。哦,对了!节点的toString()是重写过的,return getUserObject().toString;这里解释一下一面误会!
一切准备工作搞定,以下就是实现接口的细节了,代码如下:
class MyTreeCellRenderer implements TreeCellRenderer {
    public Component getTreeCellRendererComponent(JTree tree, Object value,boolean selected, boolean expanded,boolean leaf, int row,boolean hasFocus) {

        DefaultMutableTreeNode node = (DefaultMutableTreeNode)value;
        Object obj = node.getUserObject();
        if(obj instanceof JLabel) {
            return (JLabel)obj;
        }
        if(obj instanceof JCheckBox) {
            return (JCheckBox)obj;
        }
        if(obj instanceof JRadioButton) {
            return (JRadioButton)obj;
        }

        label.setText(value.toString());
        if (leaf)
            label.setIcon(leafIcon);
        else if (expanded)
            label.setIcon(openIcon);
        else
            label.setIcon(closeIcon);
        return label;
       
    }
    public void setOpenIcon (ImageIcon image) {
        openIcon = image;
    }
    public ImageIcon getOpenIcon () {
        return openIcon;
    }
    public void setClosedIcon(ImageIcon image) {
        closeIcon = image;
    }
    public ImageIcon getClosedIcon() {
        return closeIcon;
    }
    public void setLeafIcon(ImageIcon image) {
        leafIcon = image;
    }
    public ImageIcon getLeafIcon() {
        return leafIcon;
     
    private JLabel label = new JLabel();
    private boolean flag = true;
    private ImageIcon openIcon = new ImageIcon(".\\src\\image\\open.png");
    private ImageIcon closeIcon = new ImageIcon(".\\src\\image\\close.png");
    private ImageIcon leafIcon = new ImageIcon(".\\src\\image\\buddy.gif");
}

这个类把TreeCellRenderer接口实现了,注意上面着色的部分,我们把自己需要用到的组件直接返回,这样就可以在给构造node的时候直接传过去了。至于一般的图文结合或者是不能识别的类型处理方式和DefaultTreeCellRenderer一样。但是最终实现是实现了,问题也有不少,比如我们选中node后颜色不会变化,让我们无法区分是否选择等等一系列问题,最重要的是如果添加的组件,如何进行组件的操作等!以下给出了解决方案!
其实实现UI细节比较烦琐,因为你传入的控件都有自己的paint()方法,这就说明你如果要实现细节那么必然要重写这些paint()方法来配合JTree显示,既然要重写这些方法那么你所传入的控件至少是从实现了paint()重写方法的类实例化而来,这就是烦琐之处。如果大家有兴趣可以参考DefaultTreeCellRenderer类来自己写这些方法!
我想也可以通过传来的控件一个个set属性也能达到目的,但是这效率就不是很高了。唯一方便的就不用从某个控件派生而来,把共有属性都写到一堆,也不唯是个好办法。
当然DefaultTreeCellRenderer类是继承的JLabel,而我当下要用的无非就是实现每个cell有自己图片而已,所以偷了一个懒,以下是我实现部分的源码。

class MyTreeCellRenderer extends DefaultTreeCellRenderer {
    public Component getTreeCellRendererComponent(JTree tree, Object value,boolean selected, boolean expanded,boolean leaf, int row, boolean hasFocus) {
       
   DefaultMutableTreeNode node = (DefaultMutableTreeNode)value;
   Object obj = node.getUserObject();
   if(obj instanceof JLabel) {
        JLabel label = (JLabel)obj;
        DefaultTreeCellRenderer tempCellRenderer = new DefaultTreeCellRenderer();
        tempCellRenderer.setLeafIcon(label.getIcon());
        return  tempCellRenderer.getTreeCellRendererComponent(tree,label.getText(),
selected,expanded,true,row,hasFocus);
   }
   return super.getTreeCellRendererComponent(tree,value,
selected,expanded,leaf,row,hasFocus);
   }
}
目的只有一个,让实现了UI细节部分的DefaultTreeCellRenderer重用而已。这样,能实现都实现了,不能实现的也实现了,标签需要什么属性直接用tempCellRenderer的set方法即可!!虽然只有JLabel而已!呵呵。

至于如何操作控件我会放到JTable操作里面一起讲,因为他们都是实现了CellEditor接口,虽然一个是从TreeCellEditor派生,一个是从TableCellEditor派生!

 

另参考:

http://webservices.ctocio.com.cn/java/266/8956266.shtml

http://tech.ccidnet.com/art/3737/20050925/468389_1.html

http://bubble.iteye.com/blog/776388

http://blog.csdn.net/yyyzlf/article/details/4410499

http://blog.csdn.net/yyyzlf/article/details/4379857

posted @ 2011-08-21 16:15  Alexander  阅读(1887)  评论(0编辑  收藏  举报