用Java2D画出树的结构图

时间:2022-09-05 21:52:27

先看效果图:

用Java2D画出树的结构图

 

定义树的数据结构

[java] view plain copy
  1. /** 
  2.  * 2010-11-8 
  3.  * John 
  4.  */  
  5. package tree;  
  6.   
  7. import java.util.ArrayList;  
  8. import java.util.List;  
  9.   
  10. /** 
  11.  * 树的结构 
  12.  * @author John 
  13.  * 
  14.  */  
  15. public class Node {  
  16.     private String name;    //该结点名字  
  17.     private int layer = 0;  //该结点层级  
  18.   
  19.     private List<Node> childs = null//保存该结点的孩子  
  20.   
  21.     public Node(String name){  
  22.         this.name = name;  
  23.     }  
  24.       
  25.     /** 
  26.      * 增加一个孩子 
  27.      * @param n 要作为孩子增加的结点 
  28.      */  
  29.     public void add(Node n){  
  30.         if(childs == null)  
  31.             childs = new ArrayList<Node>();  
  32.         n.setLayer(layer+1);  
  33.         setChildLayout(n);  
  34.         childs.add(n);  
  35.     }  
  36.       
  37.     /** 
  38.      * 递归设置孩子的层级 
  39.      * @param n 
  40.      */  
  41.     private void setChildLayout(Node n){  
  42.         if(n.hasChild()){  
  43.             List<Node> c = n.getChilds();  
  44.             for(Node node : c){  
  45.                 node.setLayer(node.getLayer()+1);  
  46.                 setChildLayout(node);  
  47.             }  
  48.         }  
  49.     }  
  50.   
  51.     /** 
  52.      * 获取结点名 
  53.      * @return 结点名 
  54.      */  
  55.     public String getName() {  
  56.         return name;  
  57.     }  
  58.   
  59.     /** 
  60.      * 设置结点名 
  61.      * @param name 结点名 
  62.      */  
  63.     public void setName(String name) {  
  64.         this.name = name;  
  65.     }     
  66.   
  67.     /** 
  68.      * 获取该结点的层级 
  69.      * @return 该结点的层级 
  70.      */  
  71.     public int getLayer() {  
  72.         return layer;  
  73.     }  
  74.   
  75.     /** 
  76.      * 设置该结点的层级 
  77.      * @param layer 该结点的层级 
  78.      */  
  79.     public void setLayer(int layer) {  
  80.         this.layer = layer;  
  81.     }  
  82.       
  83.     /** 
  84.      * 获取该结点的孩子 
  85.      * @return 所有孩子结点 
  86.      */  
  87.     public List<Node> getChilds() {  
  88.         return childs;  
  89.     }  
  90.   
  91.     /** 
  92.      * 检查是否存在孩子 
  93.      * @return 是则返回true,否则返回false 
  94.      */  
  95.     public boolean hasChild(){  
  96.         return childs == null ? false : true;  
  97.     }  
  98.       
  99.     /** 
  100.      * 递归打印所有的结点(包括子结点) 
  101.      * @param n 要打印的根结点 
  102.      */  
  103.     public void printAllNode(Node n){  
  104.         System.out.println(n);  
  105.         if(n.hasChild()){  
  106.             List<Node> c = n.getChilds();  
  107.             for(Node node : c){  
  108.                 printAllNode(node);  
  109.             }  
  110.         }  
  111.     }  
  112.       
  113.     public String getAllNodeName(Node n){  
  114.         String s = n.toString()+"/n";  
  115.         if(n.hasChild()){  
  116.             List<Node> c = n.getChilds();  
  117.             for(Node node : c){  
  118.                 s+=getAllNodeName(node)+"/n";  
  119.             }  
  120.         }  
  121.         return s;  
  122.     }  
  123.       
  124.     public String toString(){  
  125.         return layer+"层/t: "+name;  
  126.     }  
  127. }  

 

Node的测试类:

[java] view plain copy
  1. /** 
  2.  * 2010-11-9 
  3.  * John 
  4.  */  
  5. package tree.demo;  
  6.   
  7. import tree.Node;  
  8.   
  9. /** 
  10.  * @author John 
  11.  * 
  12.  */  
  13. public class TestPrintNode {  
  14.   
  15.     public static void main(String[] args){  
  16.         Node n = new Node("root");  
  17.           
  18.         n.add(new Node("a1"));  
  19.         n.add(new Node("a2"));  
  20.           
  21.         Node n2 = new Node("a3");  
  22.         n2.add(new Node("b1"));  
  23.         n2.add(new Node("b2"));  
  24.         n2.add(new Node("b3"));  
  25.         Node n3 = new Node("b4");  
  26.         n2.add(n3);  
  27.         n3.add(new Node("c1"));  
  28.         n3.add(new Node("c2"));  
  29.   
  30.         n.add(n2);  
  31.         n.printAllNode(n);  //输出树  
  32.     }  
  33.   
  34. }  
 

 

画树的Panel

 

[java] view plain copy
  1. /** 
  2.  * 2010-11-9 
  3.  * John 
  4.  */  
  5. package tree;  
  6.   
  7. import java.awt.Color;  
  8. import java.awt.Font;  
  9. import java.awt.FontMetrics;  
  10. import java.awt.Graphics;  
  11. import java.util.List;  
  12.   
  13. import javax.swing.JPanel;  
  14.   
  15. /** 
  16.  * TODO 同一层结点过多有BUG,应该对每一层的所有结点都进行个数统计,之后才绘制。 
  17.  * @author John 
  18.  * 
  19.  */  
  20. public class TreePanel extends JPanel {  
  21.   
  22.     private Node tree;              //保存整棵树  
  23.     private int gridWidth = 80;     //每个结点的宽度  
  24.     private int gridHeight = 20;    //每个结点的高度  
  25.     private int vGap = 50;          //每2个结点的垂直距离  
  26.     private int hGap = 30;          //每2个结点的水平距离  
  27.       
  28.     private int startY = 10;        //根结点的Y,默认距离顶部10像素  
  29.     private int startX = 0;         //根结点的X,默认水平居中对齐  
  30.       
  31.     private int childAlign;                     //孩子对齐方式  
  32.     public static int CHILD_ALIGN_ABSOLUTE = 0//相对Panel居中  
  33.     public static int CHILD_ALIGN_RELATIVE = 1//相对父结点居中  
  34.       
  35.     private Font font = new Font("微软雅黑",Font.BOLD,14);  //描述结点的字体  
  36.       
  37.     private Color gridColor = Color.BLACK;      //结点背景颜色  
  38.     private Color linkLineColor = Color.BLACK;  //结点连线颜色  
  39.     private Color stringColor = Color.WHITE;    //结点描述文字的颜色  
  40.       
  41.     /** 
  42.      * 默认构造 
  43.      */  
  44.     public TreePanel(){  
  45.         this(null,CHILD_ALIGN_ABSOLUTE);  
  46.     }  
  47.       
  48.     /** 
  49.      * 根据传入的Node绘制树,以绝对居中的方式绘制 
  50.      * @param n 要绘制的树 
  51.      */  
  52.     public TreePanel(Node n){  
  53.         this(n,CHILD_ALIGN_ABSOLUTE);  
  54.     }  
  55.       
  56.     /** 
  57.      * 设置要绘制时候的对齐策略 
  58.      * @param childAlign 对齐策略 
  59.      * @see tree.TreePanel#CHILD_ALIGN_RELATIVE 
  60.      * @see tree.TreePanel#CHILD_ALIGN_ABSOLUTE 
  61.      */  
  62.     public TreePanel(int childAlign){  
  63.         this(null,childAlign);  
  64.     }  
  65.       
  66.     /** 
  67.      * 根据孩子对齐策略childAlign绘制的树的根结点n 
  68.      * @param n 要绘制的树的根结点 
  69.      * @param childAlign 对齐策略 
  70.      */  
  71.     public TreePanel(Node n, int childAlign){  
  72.         super();  
  73.         setTree(n);  
  74.         this.childAlign = childAlign;  
  75.     }  
  76.       
  77.     /** 
  78.      * 设置用于绘制的树 
  79.      * @param n 用于绘制的树的 
  80.      */  
  81.     public void setTree(Node n) {  
  82.         tree = n;  
  83.     }  
  84.       
  85.     //重写而已,调用自己的绘制方法  
  86.     public void paintComponent(Graphics g){  
  87.         startX = (getWidth()-gridWidth)/2;  
  88.         super.paintComponent(g);  
  89.         g.setFont(font);  
  90.         drawAllNode(tree, startX, g);  
  91.     }  
  92.       
  93.     /** 
  94.      * 递归绘制整棵树 
  95.      * @param n 被绘制的Node 
  96.      * @param xPos 根节点的绘制X位置 
  97.      * @param g 绘图上下文环境 
  98.      */  
  99.     public void drawAllNode(Node n, int x, Graphics g){  
  100.         int y = n.getLayer()*(vGap+gridHeight)+startY;  
  101.         int fontY = y + gridHeight - 5;     //5为测试得出的值,你可以通过FM计算更精确的,但会影响速度  
  102.           
  103.         g.setColor(gridColor);  
  104.         g.fillRect(x, y, gridWidth, gridHeight);    //画结点的格子  
  105.           
  106.         g.setColor(stringColor);  
  107.         g.drawString(n.toString(), x, fontY);       //画结点的名字  
  108.           
  109.         if(n.hasChild()){  
  110.             List<Node> c = n.getChilds();  
  111.             int size = n.getChilds().size();  
  112.             int tempPosx = childAlign == CHILD_ALIGN_RELATIVE   
  113.                          ? x+gridWidth/2 - (size*(gridWidth+hGap)-hGap)/2  
  114.                          : (getWidth() - size*(gridWidth+hGap)+hGap)/2;   
  115.               
  116.             int i = 0;  
  117.             for(Node node : c){  
  118.                 int newX = tempPosx+(gridWidth+hGap)*i; //孩子结点起始X  
  119.                 g.setColor(linkLineColor);  
  120.                 g.drawLine(x+gridWidth/2, y+gridHeight, newX+gridWidth/2, y+gridHeight+vGap);   //画连接结点的线  
  121.                 drawAllNode(node, newX, g);  
  122.                 i++;  
  123.             }  
  124.         }  
  125.     }  
  126.       
  127.     public Color getGridColor() {  
  128.         return gridColor;  
  129.     }  
  130.   
  131.     /** 
  132.      * 设置结点背景颜色 
  133.      * @param gridColor 结点背景颜色 
  134.      */  
  135.     public void setGridColor(Color gridColor) {  
  136.         this.gridColor = gridColor;  
  137.     }  
  138.   
  139.     public Color getLinkLineColor() {  
  140.         return linkLineColor;  
  141.     }  
  142.   
  143.     /** 
  144.      * 设置结点连接线的颜色 
  145.      * @param gridLinkLine 结点连接线的颜色 
  146.      */  
  147.     public void setLinkLineColor(Color gridLinkLine) {  
  148.         this.linkLineColor = gridLinkLine;  
  149.     }  
  150.   
  151.     public Color getStringColor() {  
  152.         return stringColor;  
  153.     }  
  154.   
  155.     /** 
  156.      * 设置结点描述的颜色 
  157.      * @param stringColor 结点描述的颜色 
  158.      */  
  159.     public void setStringColor(Color stringColor) {  
  160.         this.stringColor = stringColor;  
  161.     }  
  162.       
  163.     public int getStartY() {  
  164.         return startY;  
  165.     }  
  166.   
  167.     /** 
  168.      * 设置根结点的Y位置 
  169.      * @param startY 根结点的Y位置 
  170.      */  
  171.     public void setStartY(int startY) {  
  172.         this.startY = startY;  
  173.     }  
  174.   
  175.     public int getStartX() {  
  176.         return startX;  
  177.     }  
  178.   
  179.     /** 
  180.      * 设置根结点的X位置 
  181.      * @param startX 根结点的X位置 
  182.      */  
  183.     public void setStartX(int startX) {  
  184.         this.startX = startX;  
  185.     }  
  186.   
  187.       
  188. }  

 

测试TreePanel

[java] view plain copy
  1. /** 
  2.  * 2010-11-9 
  3.  * John 
  4.  */  
  5. package tree.demo;  
  6.   
  7. import java.awt.BorderLayout;  
  8. import java.awt.Color;  
  9. import java.awt.GridLayout;  
  10. import java.awt.Panel;  
  11.   
  12. import javax.swing.JFrame;  
  13. import javax.swing.JPanel;  
  14. import javax.swing.JScrollPane;  
  15. import javax.swing.JTextArea;  
  16.   
  17. import tree.Node;  
  18. import tree.TreePanel;  
  19.   
  20. /** 
  21.  * @author John 
  22.  * 
  23.  */  
  24. public class TestDrawTree extends JFrame{  
  25.       
  26.     public TestDrawTree(){  
  27.         super("Test Draw Tree");  
  28.         initComponents();  
  29.     }  
  30.       
  31.     public static void main(String[] args){  
  32.         TestDrawTree frame = new TestDrawTree();  
  33.           
  34.         frame.setSize(800600);  
  35.         frame.setVisible(true);  
  36.         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  
  37.     }   
  38.       
  39.     public void initComponents(){  
  40.         /* 
  41.          * 初始化树的数据 
  42.          */  
  43.         Node n = new Node("root");  
  44.         Node a1 = new Node("a1");  
  45.         Node a2 = new Node("a2");  
  46.         n.add(a1);  
  47.         n.add(a2);  
  48.           
  49.         Node a3 = new Node("a3");  
  50.         Node b1 = new Node("b1");  
  51.         Node d1 = new Node("d1");  
  52.         Node d2 = new Node("d2");  
  53.         b1.add(d1);  
  54.         b1.add(d2);  
  55.         a3.add(b1);  
  56.           
  57.         a3.add(new Node("b2"));  
  58.         a3.add(new Node("b3"));  
  59.         Node n3 = new Node("b4");  
  60.         a3.add(n3);  
  61.         n3.add(new Node("c1"));  
  62.         n3.add(new Node("c2"));  
  63.   
  64.         n.add(a3);  
  65.         //n.printAllNode(n);    //输出树  
  66.           
  67.         /* 
  68.          * 创建一个用于绘制树的面板并将树传入,使用相对对齐方式 
  69.          */  
  70.         TreePanel panel1 = new TreePanel(TreePanel.CHILD_ALIGN_RELATIVE);  
  71.         panel1.setTree(n);  
  72.           
  73.         /* 
  74.          * 创建一个用于绘制树的面板并将树传入,使用绝对对齐方式 
  75.          */  
  76.         TreePanel panel2 = new TreePanel(TreePanel.CHILD_ALIGN_ABSOLUTE);  
  77.         panel2.setTree(n);  
  78.         panel2.setBackground(Color.BLACK);  
  79.         panel2.setGridColor(Color.WHITE);  
  80.         panel2.setLinkLineColor(Color.WHITE);  
  81.         panel2.setStringColor(Color.BLACK);  
  82.           
  83.         JPanel contentPane = new JPanel();  
  84.         contentPane.setLayout(new GridLayout(2,1));  
  85.         contentPane.add(panel1);  
  86.         contentPane.add(panel2);  
  87.           
  88.         add(contentPane,BorderLayout.CENTER);  
  89.     }  
  90. }