• 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

  • 相关阅读:
    BZOJ 3252: 攻略(思路题)
    BZOJ 2821: 作诗(Poetize)(分块)
    BZOJ 2597: [Wc2007]剪刀石头布(费用流)
    BZOJ 1565: [NOI2009]植物大战僵尸(网络流+缩点)
    BZOJ 1927: [Sdoi2010]星际竞速(费用流)
    BZOJ 5120: [2017国家集训队测试]无限之环(费用流)
    洛谷 5205 【模板】多项式开根
    LOJ 2737 「JOISC 2016 Day 3」电报 ——思路+基环树DP
    LOJ 2736 「JOISC 2016 Day 3」回转寿司 ——堆+分块思路
    bzoj 2216 [Poi2011]Lightning Conductor——单调队列+二分处理决策单调性
  • 原文地址:https://www.cnblogs.com/bnuvincent/p/2147889.html
Copyright © 2020-2023  润新知