以JPanel组件为画板,继承JPanel类并重写paint(Graphics g)函数,在函数中使用画笔g绘制树结构图。
实例代码——3个java源文件:Main.java、DrawNode.java、DrawTree.java
1、Main.java
package drawTree;public class Main {public static void main(String[] args){DrawTree dt = new DrawTree();DrawNode dn0 = new DrawNode("纹理",0,1024);DrawNode dn0_1 = new DrawNode(dn0,"密度","清晰");DrawNode dn0_2 = new DrawNode(dn0,"触感","稍糊");DrawNode dn0_3 = new DrawNode(dn0,"no","模糊");DrawNode dn01_1 = new DrawNode(dn0_1,"no","0.4");DrawNode dn01_2 = new DrawNode(dn0_1,"yes","0.6");DrawNode dn01_3 = new DrawNode(dn0_1,"yes","0.8");DrawNode dn02_1 = new DrawNode(dn0_2,"no","硬滑");DrawNode dn02_2 = new DrawNode(dn0_2,"yes","软粘");dt.function(dn0);}
}
2、DrawNode.java
package drawTree;public class DrawNode {String value = null; //当前节点的属性DrawNode parentNode = null; //父结点String lineValue = null; //与父结点连接的属性int sonNodeNum = 0; //子结点个数DrawNode[] sonNode = new DrawNode[10]; //子结点int depth = 0; //深度int beginX = 0; //以该节点为根节点的树前边界int endX = 0; //以该节点为根节点的树后边界boolean draw = false; //是否已经被绘制int selfX = 0; //自身节点横坐标int selfY = 0; //自身节点纵坐标int parentX = 0; //父亲节点横坐标int parentY = 0; //父亲节点纵坐标DrawNode(String value,int beginX, int endX){ //根节点构造函数this.value = value;this.beginX = beginX;this.endX = endX;this.depth = 0;this.selfX = beginX + (beginX+endX)/2;this.selfY = 0;}DrawNode(DrawNode parentNode, String value, String lineValue){ //不是根节点构造函数this.parentNode = parentNode;this.value = value;this.lineValue = lineValue;this.parentX = parentNode.selfX;this.parentY = parentNode.selfY;this.depth = parentNode.depth+1;//自身x坐标是要根据父结点的子结点的个数动态改变//this.selfX = parentNode.selfX+(-200+parentNode.sonNodeNum*200/(depth+1)); //这条语句可有可无,在树完全生成后,再确定每个节点的selfXthis.selfY = parentNode.selfY+200;parentNode.sonNodeNum++;parentNode.sonNode[parentNode.sonNodeNum-1] = this;//setAllSonX(parentNode);}}
3、DrawTree.java
package drawTree;import java.awt.Graphics;
import java.awt.image.BufferedImage;import javax.swing.*;public class DrawTree {int WIDTH = 1024;int HEIGHT = 1024; DrawNode root = null;void function(DrawNode dn){JFrame jf = new JFrame();jf.setTitle("树");jf.setSize(WIDTH, HEIGHT);MyPanel mp = new MyPanel();setX(dn);mp.root = dn;jf.add(mp);jf.setVisible(true);}//递归函数,设置树中每个节点的selfXvoid setX(DrawNode root){//设置子结点的beginX和endXint gap = (root.endX-root.beginX)/(root.sonNodeNum+1);for(int i=0;i<root.sonNodeNum;i++){root.selfX = root.beginX+(root.endX-root.beginX)/2;root.sonNode[i].selfX = root.beginX+(i+1)*gap;root.sonNode[i].beginX = root.sonNode[i].selfX-gap/2;root.sonNode[i].endX = root.sonNode[i].selfX + gap/2;root.sonNode[i].parentX = root.selfX;if(root.sonNode[i]!= null){setX(root.sonNode[i]);}}}
}class MyPanel extends JPanel{DrawNode root = null;private static final long serialVersionUID = 1L;public void paint(Graphics g){//调用paint获得组件JPanel的画笔,以组件为画板DrawNode tmp = root;int x = 0;int num = 0;if(tmp != null){num = 1;}//类似非递归函数遍历树的节点while(x<num){ //int depth = tmp.depth;if(tmp.draw == false){if(tmp.sonNodeNum==0){}else{num = num + tmp.sonNodeNum;}g.drawOval(tmp.selfX, tmp.selfY, 50, 50);g.drawString(tmp.value, tmp.selfX+25, tmp.selfY+25);if(tmp != root){g.drawLine(tmp.selfX+25, tmp.selfY+25, tmp.parentX+25, tmp.parentY+25);g.drawString(tmp.lineValue, (tmp.selfX+tmp.parentX+50)/2, (tmp.selfY+tmp.parentY+50)/2);}tmp.draw = true;x++;}else{int y = -1;for(int i=0;i<tmp.sonNodeNum;i++){if(tmp.sonNode[i].draw == false){y=i;break;}}if(y!=-1){ //还有子结点为绘画。tmp = tmp.sonNode[y];continue; //进入子结点,重新循环}else{ //之下的全部结点都以绘完if(tmp ==root){break; //为根节点就退出}else{tmp = tmp.parentNode;continue;}}}if(tmp.sonNodeNum == 0){ //叶子节点,回到父结点tmp = tmp.parentNode;}else{ //不是叶子节点,进入下一层tmp = tmp.sonNode[0];}}}
}
运行结果:
新人,代码不好,多多包含。。。
之后可能会尽可能地完善一下。。
补充01:
代码运行方式:
1、放在eclipse项目的同一个包下,且三个java文件中的package要一致,即可直接运行。
2、控制台运行步骤: