先看效果图:
定义树的数据结构
[java] view plain copy- /**
- * 2010-11-8
- * John
- */
- package tree;
- import java.util.ArrayList;
- import java.util.List;
- /**
- * 树的结构
- * @author John
- *
- */
- public class Node {
- private String name; //该结点名字
- private int layer = 0; //该结点层级
- private List<Node> childs = null; //保存该结点的孩子
- public Node(String name){
- this.name = name;
- }
- /**
- * 增加一个孩子
- * @param n 要作为孩子增加的结点
- */
- public void add(Node n){
- if(childs == null)
- childs = new ArrayList<Node>();
- n.setLayer(layer+1);
- setChildLayout(n);
- childs.add(n);
- }
- /**
- * 递归设置孩子的层级
- * @param n
- */
- private void setChildLayout(Node n){
- if(n.hasChild()){
- List<Node> c = n.getChilds();
- for(Node node : c){
- node.setLayer(node.getLayer()+1);
- setChildLayout(node);
- }
- }
- }
- /**
- * 获取结点名
- * @return 结点名
- */
- public String getName() {
- return name;
- }
- /**
- * 设置结点名
- * @param name 结点名
- */
- public void setName(String name) {
- this.name = name;
- }
- /**
- * 获取该结点的层级
- * @return 该结点的层级
- */
- public int getLayer() {
- return layer;
- }
- /**
- * 设置该结点的层级
- * @param layer 该结点的层级
- */
- public void setLayer(int layer) {
- this.layer = layer;
- }
- /**
- * 获取该结点的孩子
- * @return 所有孩子结点
- */
- public List<Node> getChilds() {
- return childs;
- }
- /**
- * 检查是否存在孩子
- * @return 是则返回true,否则返回false
- */
- public boolean hasChild(){
- return childs == null ? false : true;
- }
- /**
- * 递归打印所有的结点(包括子结点)
- * @param n 要打印的根结点
- */
- public void printAllNode(Node n){
- System.out.println(n);
- if(n.hasChild()){
- List<Node> c = n.getChilds();
- for(Node node : c){
- printAllNode(node);
- }
- }
- }
- public String getAllNodeName(Node n){
- String s = n.toString()+"/n";
- if(n.hasChild()){
- List<Node> c = n.getChilds();
- for(Node node : c){
- s+=getAllNodeName(node)+"/n";
- }
- }
- return s;
- }
- public String toString(){
- return layer+"层/t: "+name;
- }
- }
Node的测试类:
[java] view plain copy- /**
- * 2010-11-9
- * John
- */
- package tree.demo;
- import tree.Node;
- /**
- * @author John
- *
- */
- public class TestPrintNode {
- public static void main(String[] args){
- Node n = new Node("root");
- n.add(new Node("a1"));
- n.add(new Node("a2"));
- Node n2 = new Node("a3");
- n2.add(new Node("b1"));
- n2.add(new Node("b2"));
- n2.add(new Node("b3"));
- Node n3 = new Node("b4");
- n2.add(n3);
- n3.add(new Node("c1"));
- n3.add(new Node("c2"));
- n.add(n2);
- n.printAllNode(n); //输出树
- }
- }
画树的Panel
[java] view plain copy
- /**
- * 2010-11-9
- * John
- */
- package tree;
- import java.awt.Color;
- import java.awt.Font;
- import java.awt.FontMetrics;
- import java.awt.Graphics;
- import java.util.List;
- import javax.swing.JPanel;
- /**
- * TODO 同一层结点过多有BUG,应该对每一层的所有结点都进行个数统计,之后才绘制。
- * @author John
- *
- */
- public class TreePanel extends JPanel {
- private Node tree; //保存整棵树
- private int gridWidth = 80; //每个结点的宽度
- private int gridHeight = 20; //每个结点的高度
- private int vGap = 50; //每2个结点的垂直距离
- private int hGap = 30; //每2个结点的水平距离
- private int startY = 10; //根结点的Y,默认距离顶部10像素
- private int startX = 0; //根结点的X,默认水平居中对齐
- private int childAlign; //孩子对齐方式
- public static int CHILD_ALIGN_ABSOLUTE = 0; //相对Panel居中
- public static int CHILD_ALIGN_RELATIVE = 1; //相对父结点居中
- private Font font = new Font("微软雅黑",Font.BOLD,14); //描述结点的字体
- private Color gridColor = Color.BLACK; //结点背景颜色
- private Color linkLineColor = Color.BLACK; //结点连线颜色
- private Color stringColor = Color.WHITE; //结点描述文字的颜色
- /**
- * 默认构造
- */
- public TreePanel(){
- this(null,CHILD_ALIGN_ABSOLUTE);
- }
- /**
- * 根据传入的Node绘制树,以绝对居中的方式绘制
- * @param n 要绘制的树
- */
- public TreePanel(Node n){
- this(n,CHILD_ALIGN_ABSOLUTE);
- }
- /**
- * 设置要绘制时候的对齐策略
- * @param childAlign 对齐策略
- * @see tree.TreePanel#CHILD_ALIGN_RELATIVE
- * @see tree.TreePanel#CHILD_ALIGN_ABSOLUTE
- */
- public TreePanel(int childAlign){
- this(null,childAlign);
- }
- /**
- * 根据孩子对齐策略childAlign绘制的树的根结点n
- * @param n 要绘制的树的根结点
- * @param childAlign 对齐策略
- */
- public TreePanel(Node n, int childAlign){
- super();
- setTree(n);
- this.childAlign = childAlign;
- }
- /**
- * 设置用于绘制的树
- * @param n 用于绘制的树的
- */
- public void setTree(Node n) {
- tree = n;
- }
- //重写而已,调用自己的绘制方法
- public void paintComponent(Graphics g){
- startX = (getWidth()-gridWidth)/2;
- super.paintComponent(g);
- g.setFont(font);
- drawAllNode(tree, startX, g);
- }
- /**
- * 递归绘制整棵树
- * @param n 被绘制的Node
- * @param xPos 根节点的绘制X位置
- * @param g 绘图上下文环境
- */
- public void drawAllNode(Node n, int x, Graphics g){
- int y = n.getLayer()*(vGap+gridHeight)+startY;
- int fontY = y + gridHeight - 5; //5为测试得出的值,你可以通过FM计算更精确的,但会影响速度
- g.setColor(gridColor);
- g.fillRect(x, y, gridWidth, gridHeight); //画结点的格子
- g.setColor(stringColor);
- g.drawString(n.toString(), x, fontY); //画结点的名字
- if(n.hasChild()){
- List<Node> c = n.getChilds();
- int size = n.getChilds().size();
- int tempPosx = childAlign == CHILD_ALIGN_RELATIVE
- ? x+gridWidth/2 - (size*(gridWidth+hGap)-hGap)/2
- : (getWidth() - size*(gridWidth+hGap)+hGap)/2;
- int i = 0;
- for(Node node : c){
- int newX = tempPosx+(gridWidth+hGap)*i; //孩子结点起始X
- g.setColor(linkLineColor);
- g.drawLine(x+gridWidth/2, y+gridHeight, newX+gridWidth/2, y+gridHeight+vGap); //画连接结点的线
- drawAllNode(node, newX, g);
- i++;
- }
- }
- }
- public Color getGridColor() {
- return gridColor;
- }
- /**
- * 设置结点背景颜色
- * @param gridColor 结点背景颜色
- */
- public void setGridColor(Color gridColor) {
- this.gridColor = gridColor;
- }
- public Color getLinkLineColor() {
- return linkLineColor;
- }
- /**
- * 设置结点连接线的颜色
- * @param gridLinkLine 结点连接线的颜色
- */
- public void setLinkLineColor(Color gridLinkLine) {
- this.linkLineColor = gridLinkLine;
- }
- public Color getStringColor() {
- return stringColor;
- }
- /**
- * 设置结点描述的颜色
- * @param stringColor 结点描述的颜色
- */
- public void setStringColor(Color stringColor) {
- this.stringColor = stringColor;
- }
- public int getStartY() {
- return startY;
- }
- /**
- * 设置根结点的Y位置
- * @param startY 根结点的Y位置
- */
- public void setStartY(int startY) {
- this.startY = startY;
- }
- public int getStartX() {
- return startX;
- }
- /**
- * 设置根结点的X位置
- * @param startX 根结点的X位置
- */
- public void setStartX(int startX) {
- this.startX = startX;
- }
- }
测试TreePanel
[java] view plain copy- /**
- * 2010-11-9
- * John
- */
- package tree.demo;
- import java.awt.BorderLayout;
- import java.awt.Color;
- import java.awt.GridLayout;
- import java.awt.Panel;
- import javax.swing.JFrame;
- import javax.swing.JPanel;
- import javax.swing.JScrollPane;
- import javax.swing.JTextArea;
- import tree.Node;
- import tree.TreePanel;
- /**
- * @author John
- *
- */
- public class TestDrawTree extends JFrame{
- public TestDrawTree(){
- super("Test Draw Tree");
- initComponents();
- }
- public static void main(String[] args){
- TestDrawTree frame = new TestDrawTree();
- frame.setSize(800, 600);
- frame.setVisible(true);
- frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
- }
- public void initComponents(){
- /*
- * 初始化树的数据
- */
- Node n = new Node("root");
- Node a1 = new Node("a1");
- Node a2 = new Node("a2");
- n.add(a1);
- n.add(a2);
- Node a3 = new Node("a3");
- Node b1 = new Node("b1");
- Node d1 = new Node("d1");
- Node d2 = new Node("d2");
- b1.add(d1);
- b1.add(d2);
- a3.add(b1);
- a3.add(new Node("b2"));
- a3.add(new Node("b3"));
- Node n3 = new Node("b4");
- a3.add(n3);
- n3.add(new Node("c1"));
- n3.add(new Node("c2"));
- n.add(a3);
- //n.printAllNode(n); //输出树
- /*
- * 创建一个用于绘制树的面板并将树传入,使用相对对齐方式
- */
- TreePanel panel1 = new TreePanel(TreePanel.CHILD_ALIGN_RELATIVE);
- panel1.setTree(n);
- /*
- * 创建一个用于绘制树的面板并将树传入,使用绝对对齐方式
- */
- TreePanel panel2 = new TreePanel(TreePanel.CHILD_ALIGN_ABSOLUTE);
- panel2.setTree(n);
- panel2.setBackground(Color.BLACK);
- panel2.setGridColor(Color.WHITE);
- panel2.setLinkLineColor(Color.WHITE);
- panel2.setStringColor(Color.BLACK);
- JPanel contentPane = new JPanel();
- contentPane.setLayout(new GridLayout(2,1));
- contentPane.add(panel1);
- contentPane.add(panel2);
- add(contentPane,BorderLayout.CENTER);
- }
- }