TWaver - 专注UI技术

http://twaver.servasoft.com/
posts - 171, comments - 191, trackbacks - 0, articles - 2
  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

震撼的CSDN用户关系图

Posted on 2010-09-07 14:11 TWaver 阅读(2941) 评论(4)  编辑  收藏

前言

在一次校友聚会上,一个做到世界500强公司高级管理层的校友说:在这个城市,要随便找到一个人扯上关系,我最多通过三个人就能办到。这个社会老混混语出惊人,其道理却深值思考。这个社会是一个人与人组成的巨大的关系网络,每个人都是这个巨大网络中的一个节点。如能充分认识和利用这个网络,我们的工作和事业必将如虎添翼。

相信大家都有这样的经历,为了了解软件行业信息或解决开发难题时,都会求搜索CSDN 或Javaeye上的专家。同样,某些时候我们也会通过BLOG或发帖共享自己的知识点,不知不觉成为帮助别人的“专家”。而这些网站用户之间又有“关注”关系、“好友”关系等,其实这也是一个巨大的人际网络、知识网络。这个网络到底有多大,看上去又会如何呢?是否也能大到“三个人就能到达任意点”的程度呢?怀着无比的好奇心情,我萌发了一个念头:用程序图形化的呈现一下这个庞大的人际网络,相信一定会have lots of fun!

想法出来后,首先仔细观察了一下CSDN网站的BLOG页面结构和好友信息列表,然后用Java Swing写了一个简单的程序。通过近一周的修改,终于初具雏形!在此首先衷心感谢CSDN提供了这么好的数据,其次感谢博客里的朋友,是让这个图出来就这么好看,是你们给了我无穷的动力!

废话不说,先上一张最终效果图:

继续阐述一下程序设计思路。
技术准备
自己最熟悉Java Swing,于是毫无疑问,用Swing来做。同时对需要图形化呈现,自然是用我比较熟悉的TWaver Java版。准备好JDK6和Netbeans,开始干活。

首先设置字体。没办法,我有“雅黑”强迫症,自从听说微软每个雅黑汉字都花费了100美金之后,看见什么都想先弄成雅黑,这里也不例外。还用XP的朋友就不好意思了,不知道字体显示效果如何。在Swing里面设置起来很简单,这里偷懒挑一下,只把几个用到的Component设置字体,然后用SwingUtilities.invokeLater启动主窗体:

 1public static void main(String[] args) {
 2    SwingUtilities.invokeLater(new Runnable() {
 3
 4        public void run() {
 5            //setup swing fonts before start program.
 6            Font font = new Font("微软雅黑", Font.PLAIN, 12);
 7            UIManager.put("Label.font", font);
 8            UIManager.put("Button.font", font);
 9            UIManager.put("RadioButton.font", font);
10            UIManager.put("CheckBox.font", font);
11            UIManager.put("TextField.font", font);
12
13            //show main ui.
14            MainUI ui = new MainUI();
15            ui.setVisible(true);
16        }

17    }
);
18}

是我喜欢的雅黑效果,耶!


抓取网页数据
接下来,要解决如何获取CSDN用户信息以及好友信息的问题。观察CSDN网站可以发现,用户的BLOG首页是http://blog.csdn.net/+用户名。例如我的BLOG地址是http://blog.csdn.net/solo。要看本人好友,需要看本人详细信息页面,这个页面URL是:http://hi.csdn.net/solo。在这个页面的右侧有用户好友列表。

这样,我们就可以用URL和流来读取页面,并解析其中的好友了。在浏览器查看HTML源码,确定好友列表对应的HTML标志,然后通过以下代码进行解析:

 1try {
 2            URL url = new URL(urlString);
 3            BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8"));
 4            String line = reader.readLine();
 5            while (line != null{
 6                line = line.trim();
 7                if (line.startsWith("<div id=\"space_avatar\">")) {
 8                    line = reader.readLine().trim();
 9                    int index = line.indexOf("http");
10                    line = line.substring(index);
11                    index = line.indexOf("\"");
12                    String imageURL = line.substring(0, index);
13                    index = line.indexOf("alt=");
14                    line = line.substring(index + 5);
15                    if (line.contains("\"")) {
16                        line = line.substring(0, line.indexOf("\""));
17                    }

18                    String tooltip = line;
19
20                    UserNode centerNode = addNode(null, tooltip, imageURL, null);
21                    return centerNode;
22                }

23
24                line = reader.readLine();
25            }

26        } catch (Exception ex) {
27            //ex.printStackTrace();
28        }

此外,为了防止读取URL的过程阻塞Swing线程造成界面卡壳,把它封装并放在单独的Thread或Runnable中进行,读取结果后,再将结果动态放入界面即可:

 1public class PageExplorer implements Runnable {
 2.
 3    public void run() {
 4        try {
 5            if (parent == null{
 6                UserNode centerNode = createCenterNode();
 7                addChildrenNode(centerNode);
 8                centerNode.setExplored();
 9            }
 else {
10                addChildrenNode(parent);
11                parent.setExplored();
12            }

13        }
 catch (Exception ex) {
14            JOptionPane.showMessageDialog(network, "无法获得该用户数据。");
15        }

16    }

17..
18}

数据显示
数据获得后,如何显示是关键。显示效果一定要直观、美观、容易理解。TWaver的拓扑图是不二选择,全图形化的拓扑结构绝对比表格之类的东西更加直观、讨巧。然后定义图形元素。其实只有两个元素,一个是点,一个是线。点表示用户节点,线表示其关系,这里只显示一个简单的朋友关系。

接下来就用TWaver的Node和Link定义两个类,封装节点和连线:

 1public class UserNode extends ResizableNode {
 2
 3    private boolean male = !(TWaverUtil.getRandomInt(5== 0);
 4
 5    public UserNode() {
 6        init();
 7    }

 8
 9    private void init() {
10        this.putBorderVisible(false);
11
12        this.putCustomDraw(true);
13        this.putCustomDrawFill(true);
14        this.putCustomDrawGradient(false);
15        this.putCustomDrawGradient(true);
16
17        this.putLabelColor(Color.white);
18        this.putLabelFont(new Font("微软雅黑", Font.PLAIN, 12));
19        this.putLabelYOffset(-5);
20        this.putLabelHighlightable(false);
21
22        this.putLabelUnderlineColor(Color.white);
23        if (male) {
24            this.setSize(1010);
25            this.putCustomDrawGradient(false);
26            this.putCustomDrawFill3D(true);
27            this.putCustomDrawOutline(false);
28            this.putCustomDrawShapeFactory(TWaverConst.SHAPE_RECTANGLE);
29            this.putCustomDrawFillColor(Color.green.darker());
30        }
 else {
31            this.setSize(1515);
32            this.putCustomDrawShapeFactory(TWaverConst.SHAPE_CIRCLE);
33            this.putCustomDrawOutline(false);
34            this.putCustomDrawGradientFactory(TWaverConst.GRADIENT_LINE_NE);
35            this.putCustomDrawGradientColor(Color.yellow.brighter());
36            this.putCustomDrawFillColor(Color.orange);
37        }

38    }

39..
40}
试了一下自己的朋友关系,效果还不错,可惜就是好友太少了!

顺藤摸瓜
光显示自己的关系网自然不够,还要能够顺藤摸瓜不断的展开、延伸下去才行。基本思路简单:双击下一个节点,再用前面的方法抓取这个人的网页URL,并将下一层好友再次填入,不断往复、以此类推。这样试了一下,有点意思了:


自动布局
数据复杂后,没有很好的组织结构是无法看出效果的。TWaver提供了不错的自动布局算法,我就利用了两个:一个是环形的静态自动布局,布局后会静止不动;另外一个是基于弹簧算法的动态布局,节点会动画一样的慢慢调整,很有意思。同时提供了两个按钮进行布局切换。弹簧布局比较生动,在拖动节点过程中可以呈现出不同的姿态:

弹簧布局的参数如下:

 1//setup TWaver auto-layout algorithm parameters.
 2        network.getSpringLayouter().setForceSize(3);
 3        network.getSpringLayouter().setStepSize(40);
 4        network.getSpringLayouter().setNodeRepulsionFactor(1);
 5        network.getSpringLayouter().setLinkRepulsionFactor(30);
 6        network.getSpringLayouter().start();
 7        network.getCanvasScrollPane().setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
 8        network.getCanvasScrollPane().setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
 9
10        //when window resized, reset the spring layout limit bounds to canvas view port size.
11        network.addComponentListener(new ComponentAdapter() {
12
13            @Override
14            public void componentResized(ComponentEvent e) {
15                network.getSpringLayouter().setLimitBounds(network.getCanvasScrollPane().getBounds());
16            }

17        }
);

绘制说明文字
想添加一点说明文字,要不然大家都不知道怎么用。传统文字说明太土了,自然不符合本程序的审美和风格。要弄就弄fashion一点。想到了TWaver的Marker机制。这个可以在拓扑图上面任意paint东西,符合我的要求!于是fill一个rectangle然后draw文字:

 1public class NoteMarker implements CanvasMarker {
 2
 3    private Color backgroundColor = new Color(020020050);
 4    private Font font = new Font("微软雅黑", Font.BOLD, 12);
 5
 6    public void mark(Graphics2D g) {
 7        g.setColor(backgroundColor);
 8        g.fill3DRect(5050330150true);
 9        g.setFont(font);
10        g.setColor(Color.white);
11        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
12        int space = 20;
13        int x = 55;
14        int y = 65;
15        g.drawString("1、双击彩色节点【绿色或黄色】进行关系展开", x, y);
16        y += space;
17        g.drawString("2、灰色节点为已探索用户", x, y);
18        y += space;
19        g.drawString("3、绿色节点为Boy,黄色节点为Girl", x, y);
20        y += space;
21        g.drawString("4、鼠标停留可以tooltip此人的照片和BLOG网址", x, y);
22        y += space;
23        g.drawString("5、选择“环形布局”或“弹簧布局”进行布局算法切换", x, y);
24        y += space;
25        g.drawString("6、点击“随机展开几个”按钮随机对几个节点自动展开", x, y);
26        y += space;
27        g.drawString("7、文本框输入其他CSDN用户名并点击按钮“重新开始”", x, y);
28    }

29}

再通过下面代码安装marker:

1//display notes on network canvas with a marker.
2network.addCanvasMarker(new NoteMarker());

效果如下:

另外,添加了一个小功能:在鼠标停留一个节点一点时间后,通过tooltip显示此人的照片和URL地址。同时有个惊人的发现:这里竟然可以支持GIF动画!


最终效果
为了展开方便,添加了一个按钮,可以随意展开几个节点,节省了不少鼠标操作。一阵狂点之后,还真是看出了“人与人的关系”之复杂性了:

去掉人的名字,效果如下:


程序及源代码下载

老规矩,有福同享、有难同当。源代码和可执行jar包自然会在此奉上。有兴趣的朋友,可以在此基础上,继续完善功能。哪个哥们有精力的话,可以再做一个JavaEye版的就好了!欢迎大家就此进行讨论!另外如果有时间,我还想做一个Flex版的JavaEye的例子,有兴趣的朋友可以共同参与。
可执行程序(jar包、run.bat)和源代码(java源文件)在此处下载:
请确保使用JDK 6编译和运行,同时保持网络通畅,以便到CSDN网站进行数据抓取。
谢谢!

评论

# re: 震撼的CSDN用户关系图[未登录]  回复  更多评论   

2010-09-07 14:56 by 111
楼主做个博客园的呀

# re: 楼主做个博客园的呀  回复  更多评论   

2010-09-07 20:57 by lexus
严重同意楼上观点,twaver再来一个博客园的关系网

# re: 震撼的CSDN用户关系图  回复  更多评论   

2010-09-08 01:38 by bonamana
这个东西很久很久就看到了

# re: 震撼的CSDN用户关系图  回复  更多评论   

2010-09-08 09:24 by Jos
@lexus
hi,不知道博客园是否有相应的API公开出来啊?很想试试!

只有注册用户登录后才能发表评论。


网站导航: