以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)); //这条语句可有可无,在树完全生成后,再确定每个节点的selfX
this.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);
}
//递归函数,设置树中每个节点的selfX
void setX(DrawNode root){
//设置子结点的beginX和endX
int 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、控制台运行步骤: