伴随着前期的基础积累,翻过API,读过一些Demo,总觉得自己已经摸透了Prefuse,小打小闹似乎已经无法满足内心膨胀的自己。还记得儿时看的《武状元苏乞儿》中降龙十八掌最后一张居然是空白页,在千钧一发之际以为自己要嗝屁了,一阵东风让苏乞儿明白了,最后一章要做的原来是——整合。没错,今天我们就来好好谈谈整合!

  看懂API不代表你会灵活运用某个类,会用一些类不代表能实现小功能,耍的了小功能不一定会做可视化工具整合,整合之道,奥妙无穷!

  前篇回顾:上篇《漫谈可视化Prefuse(四)---被玩坏的Prefuse API》主要从Prefuse API角度,单线条的依据API之道,对部分类进行剖析,通过coding实现小功能并辅之于图为证,大致提供了一条学习Prefuse的路线。期间也有众多志同道合之友前来了解Prefuse到底为何物、能做什么,问答之间着实让人兴奋不已。

  本篇作为在博客园里漫谈可视化Prefuse系列的收尾篇,主要看看自己对于Prefuse的粗劣的整合,重点介绍下自己在整合期间印象较为深刻的片段和coding瞬间。其中包括有对于单例模式、匿名内部类、源码改动等认识。

  1.理想与现实——总会有差距

  需求催生产品,产品总是滞后不断更新的需求。

  需求:一款visualization tool,像Gephi那样炫酷,像i2那样流畅强大;

  产品:不断更迭中

  问题:需要全手工搭建,了解Swing,熟悉Prefuse,学会借鉴,深谙拼凑整合之道

  功能(目前):数据导入+连接数据库+图片另存为+布局算法切换+节点标签显示+节点形状改变+适合屏幕显示+高亮近邻显示+控制图形布局开关  等等

  产品部分截图:

  (1)主界面

  

  界面顶部是实现功能的菜单栏(当然还有裹在各个菜单栏里面的菜单选项);

  界面左边是信息提示框,用于显示导入图形的节点和边的相关信息,信息提示面板中还有一个隐藏的Label用于显示有向/无向/混合图信息以及一个控制布局的开关(初始状态为灰,不可用);

  界面的核心部分是一个JSplitPanel,用于图形的展示;

  整个图形的布局采用GridBagLayout,无需为工具因为缩放以及最大化最小化而导致控件大小不能动态调整而烦恼

  (2)文件菜单栏

  

  其中功能有"打开...":用于打开指定文件并导入数据

  连接数据库:用于连接Sql server2005数据库,从中读取点和边信息并展示图形,详情点击这里。

  存储为:初始状态为灰不可用,因为此时没有导入图形,当图形导入并展现后便可变为正常可用。用于存储展示图形

  退出:exit tool

  其他菜单栏不一一展示,在图形为加载到面板时,大多数为灰不可用状态。

  布局算法菜单栏:力导向布局、圆形布局、随机布局、网格布局、FruchtermanReingold布局

  标签配置菜单栏:不显示、显示文本、显示文本与图片

  节点配置菜单栏:圆形、矩形、圆角矩形

  控制器菜单栏:适应屏幕显示

  2.源码动一动——让不可能成为可能

  作为老产品,想要与Gephi等同台竞技,不仅要拼实力,更要拼灵活应对,通过读烂API都无法解决的问题,换个思维,动一动源码就会豁然开朗。

  (1)节点形状

  源码中,Prefuse图形展示的节点是矩形或是圆角矩形,怎么看怎么觉得有点突兀,不柔和,相比之下Gephi中的圆形看上去要舒服得多。那么问题来了……如何完成这个功能,仅仅通过传参等手段已经不够用了,所以顺藤摸瓜,找到LabelRenderer类

package prefuse.render;import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Arc2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RectangularShape;
import java.awt.geom.RoundRectangle2D;import prefuse.Constants;
import prefuse.util.ColorLib;
import prefuse.util.FontLib;
import prefuse.util.GraphicsLib;
import prefuse.util.StringLib;
import prefuse.visual.VisualItem;/**渲染一个包含或文本或图片或兼有之的label标签* Renderer that draws a label, which consists of a text string,* an image, or both.* * <p>When created using the default constructor, the renderer attempts* to use text from the "label" field. To use a different field, use the* appropriate constructor or use the {@link #setTextField(String)} method.* To perform custom String selection, subclass this Renderer and override the * {@link #getText(VisualItem)} method. When the text field is* <code>null</code>, no text label will be shown. Labels can span multiple* lines of text, determined by the presence of newline characters ('\n')* within the text string.</p>* * <p>By default, no image is shown. To show an image, the image field needs* to be set, either using the appropriate constructor or the* {@link #setImageField(String)} method. The value of the image field should* be a text string indicating the location of the image file to use. The* string should be either a URL, a file located on the current classpath,* or a file on the local filesystem. If found, the image will be managed* internally by an {@link ImageFactory} instance, which maintains a* cache of loaded images.</p>* * <p>The position of the image relative to text can be set using the* {@link #setImagePosition(int)} method. Images can be placed to the* left, right, above, or below the text. The horizontal and vertical* alignments of either the text or the image can be set explicitly* using the appropriate methods of this class (e.g.,* {@link #setHorizontalTextAlignment(int)}). By default, both the* text and images are centered along both the horizontal and* vertical directions.</p>* * @author <a href="http://jheer.org">jeffrey heer</a>*/
public class LabelRenderer extends AbstractShapeRenderer {protected ImageFactory m_images = null;protected String m_delim = "\n";protected String m_labelName = "label";protected String m_imageName = null;protected int m_xAlign = Constants.CENTER;protected int m_yAlign = Constants.CENTER;protected int m_hTextAlign = Constants.CENTER;protected int m_vTextAlign = Constants.CENTER;protected int m_hImageAlign = Constants.CENTER;protected int m_vImageAlign = Constants.CENTER;protected int m_imagePos = Constants.LEFT;protected int m_horizBorder = 2;protected int m_vertBorder  = 0;protected int m_imageMargin = 2;protected int m_arcWidth    = 0;protected int m_arcHeight   = 0;protected int m_maxTextWidth = -1;/** Transform used to scale and position images */AffineTransform m_transform = new AffineTransform();/** The holder for the currently computed bounding box */protected RectangularShape m_bbox  = new Rectangle2D.Double();    // Prefuse框架自带的,即默认使用矩形protected Point2D m_pt = new Point2D.Double(); // temp pointprotected Font    m_font; // temp font holderprotected String    m_text; // label textprotected Dimension m_textDim = new Dimension(); // text width / height/*** Create a new LabelRenderer. By default the field "label" is used* as the field name for looking up text, and no image is used.*/public LabelRenderer() {}/*** Create a new LabelRenderer. Draws a text label using the given* text data field and does not draw an image.* @param textField the data field for the text label.*/public LabelRenderer(String textField) {this.setTextField(textField);}/*** Create a new LabelRenderer. Draws a text label using the given text* data field, and draws the image at the location reported by the* given image data field.* @param textField the data field for the text label* @param imageField the data field for the image location. This value* in the data field should be a URL, a file within the current classpath,* a file on the filesystem, or null for no image. If the* <code>imageField</code> parameter is null, no images at all will be* drawn.*/public LabelRenderer(String textField, String imageField) {setTextField(textField);setImageField(imageField);}// ------------------------------------------------------------------------/*** Rounds the corners of the bounding rectangle in which the text* string is rendered. This will only be seen if either the stroke* or fill color is non-transparent.* @param arcWidth the width of the curved corner* @param arcHeight the height of the curved corner*/public void setRoundedCorner(int arcWidth, int arcHeight) {if ( (arcWidth == 0 || arcHeight == 0) && !(m_bbox instanceof Rectangle2D) ) {m_bbox = new Rectangle2D.Double();} else {if ( !(m_bbox instanceof RoundRectangle2D) )m_bbox = new RoundRectangle2D.Double();((RoundRectangle2D)m_bbox).setRoundRect(0,0,10,10,arcWidth,arcHeight);m_arcWidth = arcWidth;m_arcHeight = arcHeight;}}/*** Get the field name to use for text labels.* @return the data field for text labels, or null for no text*/public String getTextField() {return m_labelName;}/*** Set the field name to use for text labels.* @param textField the data field for text labels, or null for no text*/public void setTextField(String textField) {m_labelName = textField;}/*** Sets the maximum width that should be allowed of the text label.* A value of -1 specifies no limit (this is the default).* @param maxWidth the maximum width of the text or -1 for no limit*/public void setMaxTextWidth(int maxWidth) {m_maxTextWidth = maxWidth;}/*** Returns the text to draw. Subclasses can override this class to* perform custom text selection.* @param item the item to represent as a <code>String</code>* @return a <code>String</code> to draw*/protected String getText(VisualItem item) {String s = null;if ( item.canGetString(m_labelName) ) {return item.getString(m_labelName);            }return s;}// ------------------------------------------------------------------------// Image Handling/*** Get the data field for image locations. The value stored* in the data field should be a URL, a file within the current classpath,* a file on the filesystem, or null for no image.* @return the data field for image locations, or null for no images*/public String getImageField() {return m_imageName;}/*** Set the data field for image locations. The value stored* in the data field should be a URL, a file within the current classpath,* a file on the filesystem, or null for no image. If the* <code>imageField</code> parameter is null, no images at all will be* drawn.* @param imageField the data field for image locations, or null for* no images*/public void setImageField(String imageField) {if ( imageField != null ) m_images = new ImageFactory();m_imageName = imageField;}/*** Sets the maximum image dimensions, used to control scaling of loaded* images. This scaling is enforced immediately upon loading of the image.* @param width the maximum width of images (-1 for no limit)* @param height the maximum height of images (-1 for no limit)*/public void setMaxImageDimensions(int width, int height) {if ( m_images == null ) m_images = new ImageFactory();m_images.setMaxImageDimensions(width, height);}/*** Returns a location string for the image to draw. Subclasses can override * this class to perform custom image selection beyond looking up the value* from a data field.* @param item the item for which to select an image to draw* @return the location string for the image to use, or null for no image*/protected String getImageLocation(VisualItem item) {return item.canGetString(m_imageName)? item.getString(m_imageName): null;}/*** Get the image to include in the label for the given VisualItem.* @param item the item to get an image for* @return the image for the item, or null for no image*/protected Image getImage(VisualItem item) {String imageLoc = getImageLocation(item);return ( imageLoc == null ? null : m_images.getImage(imageLoc) );}// ------------------------------------------------------------------------// Renderingprivate String computeTextDimensions(VisualItem item, String text,double size){// put item font in temp member variablem_font = item.getFont();// scale the font as neededif ( size != 1 ) {m_font = FontLib.getFont(m_font.getName(), m_font.getStyle(),size*m_font.getSize());}FontMetrics fm = DEFAULT_GRAPHICS.getFontMetrics(m_font);StringBuffer str = null;// compute the number of lines and the maximum widthint nlines = 1, w = 0, start = 0, end = text.indexOf(m_delim);m_textDim.width = 0;String line;for ( ; end >= 0; ++nlines ) {w = fm.stringWidth(line=text.substring(start,end));// abbreviate line as neededif ( m_maxTextWidth > -1 && w > m_maxTextWidth ) {if ( str == null )str = new StringBuffer(text.substring(0,start));str.append(StringLib.abbreviate(line, fm, m_maxTextWidth));str.append(m_delim);w = m_maxTextWidth;} else if ( str != null ) {str.append(line).append(m_delim);}// update maximum width and substring indicesm_textDim.width = Math.max(m_textDim.width, w);start = end+1;end = text.indexOf(m_delim, start);}w = fm.stringWidth(line=text.substring(start));// abbreviate line as neededif ( m_maxTextWidth > -1 && w > m_maxTextWidth ) {if ( str == null )str = new StringBuffer(text.substring(0,start));str.append(StringLib.abbreviate(line, fm, m_maxTextWidth));w = m_maxTextWidth;} else if ( str != null ) {str.append(line);}// update maximum widthm_textDim.width = Math.max(m_textDim.width, w);// compute the text heightm_textDim.height = fm.getHeight() * nlines;return str==null ? text : str.toString();}/*** @see prefuse.render.AbstractShapeRenderer#getRawShape(prefuse.visual.VisualItem)*/protected Shape getRawShape(VisualItem item) {m_text = getText(item);Image  img  = getImage(item);double size = item.getSize();// get image dimensionsdouble iw=0, ih=0;if ( img != null ) {ih = img.getHeight(null);iw = img.getWidth(null);    }// get text dimensionsint tw=0, th=0;if ( m_text != null ) {m_text = computeTextDimensions(item, m_text, size);th = m_textDim.height;tw = m_textDim.width;   }// get bounding box dimensionsdouble w=0, h=0;switch ( m_imagePos ) {case Constants.LEFT:case Constants.RIGHT:w = tw + size*(iw +2*m_horizBorder+ (tw>0 && iw>0 ? m_imageMargin : 0));h = Math.max(th, size*ih) + size*2*m_vertBorder;break;case Constants.TOP:case Constants.BOTTOM:w = Math.max(tw, size*iw) + size*2*m_horizBorder;h = th + size*(ih + 2*m_vertBorder+ (th>0 && ih>0 ? m_imageMargin : 0));break;default:throw new IllegalStateException("Unrecognized image alignment setting.");}// get the top-left point, using the current alignment settingsgetAlignedPoint(m_pt, item, w, h, m_xAlign, m_yAlign); if ( m_bbox instanceof RoundRectangle2D ) {RoundRectangle2D rr = (RoundRectangle2D)m_bbox;rr.setRoundRect(m_pt.getX(), m_pt.getY(), w, h,size*m_arcWidth, size*m_arcHeight);} else {m_bbox.setFrame(m_pt.getX(), m_pt.getY(), w, h);}                                                               return m_bbox;}/*** Helper method, which calculates the top-left co-ordinate of an item* given the item's alignment.*/protected static void getAlignedPoint(Point2D p, VisualItem item, double w, double h, int xAlign, int yAlign){double x = item.getX(), y = item.getY();if ( Double.isNaN(x) || Double.isInfinite(x) )x = 0; // safety checkif ( Double.isNaN(y) || Double.isInfinite(y) )y = 0; // safety checkif ( xAlign == Constants.CENTER ) {x = x-(w/2);} else if ( xAlign == Constants.RIGHT ) {x = x-w;}if ( yAlign == Constants.CENTER ) {y = y-(h/2);} else if ( yAlign == Constants.BOTTOM ) {y = y-h;}p.setLocation(x,y);}/*** @see prefuse.render.Renderer#render(java.awt.Graphics2D, prefuse.visual.VisualItem)*/public void render(Graphics2D g, VisualItem item) {RectangularShape shape = (RectangularShape)getShape(item);if ( shape == null ) return;// fill the shape, if requestedint type = getRenderType(item);if ( type==RENDER_TYPE_FILL || type==RENDER_TYPE_DRAW_AND_FILL )GraphicsLib.paint(g, item, shape, getStroke(item), RENDER_TYPE_FILL);// now render the image and textString text = m_text;Image  img  = getImage(item);if ( text == null && img == null )return;double size = item.getSize();boolean useInt = 1.5 > Math.max(g.getTransform().getScaleX(),g.getTransform().getScaleY());double x = shape.getMinX() + size*m_horizBorder;double y = shape.getMinY() + size*m_vertBorder;// render imageif ( img != null ) {            double w = size * img.getWidth(null);double h = size * img.getHeight(null);double ix=x, iy=y;// determine one co-ordinate based on the image positionswitch ( m_imagePos ) {case Constants.LEFT:x += w + size*m_imageMargin;break;case Constants.RIGHT:ix = shape.getMaxX() - size*m_horizBorder - w;break;case Constants.TOP:y += h + size*m_imageMargin;break;case Constants.BOTTOM:iy = shape.getMaxY() - size*m_vertBorder - h;break;default:throw new IllegalStateException("Unrecognized image alignment setting.");}// determine the other coordinate based on image alignmentswitch ( m_imagePos ) {case Constants.LEFT:case Constants.RIGHT:// need to set image y-coordinateswitch ( m_vImageAlign ) {case Constants.TOP:break;case Constants.BOTTOM:iy = shape.getMaxY() - size*m_vertBorder - h;break;case Constants.CENTER:iy = shape.getCenterY() - h/2;break;}break;case Constants.TOP:case Constants.BOTTOM:// need to set image x-coordinateswitch ( m_hImageAlign ) {case Constants.LEFT:break;case Constants.RIGHT:ix = shape.getMaxX() - size*m_horizBorder - w;break;case Constants.CENTER:ix = shape.getCenterX() - w/2;break;}break;}if ( useInt && size == 1.0 ) {// if possible, use integer precision// results in faster, flicker-free image renderingg.drawImage(img, (int)ix, (int)iy, null);} else {m_transform.setTransform(size,0,0,size,ix,iy);g.drawImage(img, m_transform, null);}}// render textint textColor = item.getTextColor();if ( text != null && ColorLib.alpha(textColor) > 0 ) {g.setPaint(ColorLib.getColor(textColor));g.setFont(m_font);FontMetrics fm = DEFAULT_GRAPHICS.getFontMetrics(m_font);// compute available widthdouble tw;switch ( m_imagePos ) {case Constants.TOP:case Constants.BOTTOM:tw = shape.getWidth() - 2*size*m_horizBorder;break;default:tw = m_textDim.width;}// compute available heightdouble th;switch ( m_imagePos ) {case Constants.LEFT:case Constants.RIGHT:th = shape.getHeight() - 2*size*m_vertBorder;break;default:th = m_textDim.height;}// compute starting y-coordinatey += fm.getAscent();switch ( m_vTextAlign ) {case Constants.TOP:break;case Constants.BOTTOM:y += th - m_textDim.height;break;case Constants.CENTER:y += (th - m_textDim.height)/2;}// render each line of textint lh = fm.getHeight(); // the line heightint start = 0, end = text.indexOf(m_delim);for ( ; end >= 0; y += lh ) {drawString(g, fm, text.substring(start, end), useInt, x, y, tw);start = end+1;end = text.indexOf(m_delim, start);   }drawString(g, fm, text.substring(start), useInt, x, y, tw);}// draw borderif (type==RENDER_TYPE_DRAW || type==RENDER_TYPE_DRAW_AND_FILL) {GraphicsLib.paint(g,item,shape,getStroke(item),RENDER_TYPE_DRAW);}}private final void drawString(Graphics2D g, FontMetrics fm, String text,boolean useInt, double x, double y, double w){// compute the x-coordinatedouble tx;switch ( m_hTextAlign ) {case Constants.LEFT:tx = x;break;case Constants.RIGHT:tx = x + w - fm.stringWidth(text);break;case Constants.CENTER:tx = x + (w - fm.stringWidth(text)) / 2;break;default:throw new IllegalStateException("Unrecognized text alignment setting.");}// use integer precision unless zoomed-in// results in more stable drawingif ( useInt ) {g.drawString(text, (int)tx, (int)y);} else {g.drawString(text, (float)tx, (float)y);}}/*** Returns the image factory used by this renderer.* @return the image factory*/public ImageFactory getImageFactory() {if ( m_images == null ) m_images = new ImageFactory();return m_images;}/*** Sets the image factory used by this renderer.* @param ifact the image factory*/public void setImageFactory(ImageFactory ifact) {m_images = ifact;}// ------------------------------------------------------------------------/*** Get the horizontal text alignment within the layout. One of* {@link prefuse.Constants#LEFT}, {@link prefuse.Constants#RIGHT}, or* {@link prefuse.Constants#CENTER}. The default is centered text.* @return the horizontal text alignment*/public int getHorizontalTextAlignment() {return m_hTextAlign;}/*** Set the horizontal text alignment within the layout. One of* {@link prefuse.Constants#LEFT}, {@link prefuse.Constants#RIGHT}, or* {@link prefuse.Constants#CENTER}. The default is centered text.* @param halign the desired horizontal text alignment*/public void setHorizontalTextAlignment(int halign) {if ( halign != Constants.LEFT &&halign != Constants.RIGHT &&halign != Constants.CENTER )throw new IllegalArgumentException("Illegal horizontal text alignment value.");m_hTextAlign = halign;}/*** Get the vertical text alignment within the layout. One of* {@link prefuse.Constants#TOP}, {@link prefuse.Constants#BOTTOM}, or* {@link prefuse.Constants#CENTER}. The default is centered text.* @return the vertical text alignment*/public int getVerticalTextAlignment() {return m_vTextAlign;}/*** Set the vertical text alignment within the layout. One of* {@link prefuse.Constants#TOP}, {@link prefuse.Constants#BOTTOM}, or* {@link prefuse.Constants#CENTER}. The default is centered text.* @param valign the desired vertical text alignment*/public void setVerticalTextAlignment(int valign) {if ( valign != Constants.TOP &&valign != Constants.BOTTOM &&valign != Constants.CENTER )throw new IllegalArgumentException("Illegal vertical text alignment value.");m_vTextAlign = valign;}/*** Get the horizontal image alignment within the layout. One of* {@link prefuse.Constants#LEFT}, {@link prefuse.Constants#RIGHT}, or* {@link prefuse.Constants#CENTER}. The default is a centered image.* @return the horizontal image alignment*/public int getHorizontalImageAlignment() {return m_hImageAlign;}/*** Set the horizontal image alignment within the layout. One of* {@link prefuse.Constants#LEFT}, {@link prefuse.Constants#RIGHT}, or* {@link prefuse.Constants#CENTER}. The default is a centered image.* @param halign the desired horizontal image alignment*/public void setHorizontalImageAlignment(int halign) {if ( halign != Constants.LEFT &&halign != Constants.RIGHT &&halign != Constants.CENTER )throw new IllegalArgumentException("Illegal horizontal text alignment value.");m_hImageAlign = halign;}/*** Get the vertical image alignment within the layout. One of* {@link prefuse.Constants#TOP}, {@link prefuse.Constants#BOTTOM}, or* {@link prefuse.Constants#CENTER}. The default is a centered image.* @return the vertical image alignment*/public int getVerticalImageAlignment() {return m_vImageAlign;}/*** Set the vertical image alignment within the layout. One of* {@link prefuse.Constants#TOP}, {@link prefuse.Constants#BOTTOM}, or* {@link prefuse.Constants#CENTER}. The default is a centered image.* @param valign the desired vertical image alignment*/public void setVerticalImageAlignment(int valign) {if ( valign != Constants.TOP &&valign != Constants.BOTTOM &&valign != Constants.CENTER )throw new IllegalArgumentException("Illegal vertical text alignment value.");m_vImageAlign = valign;}/*** Get the image position, determining where the image is placed with* respect to the text. One of {@link Constants#LEFT},* {@link Constants#RIGHT}, {@link Constants#TOP}, or* {@link Constants#BOTTOM}.  The default is left.* @return the image position*/public int getImagePosition() {return m_imagePos;}/*** Set the image position, determining where the image is placed with* respect to the text. One of {@link Constants#LEFT},* {@link Constants#RIGHT}, {@link Constants#TOP}, or* {@link Constants#BOTTOM}.  The default is left.* @param pos the desired image position*/public void setImagePosition(int pos) {if ( pos != Constants.TOP &&pos != Constants.BOTTOM &&pos != Constants.LEFT &&pos != Constants.RIGHT &&pos != Constants.CENTER )throw new IllegalArgumentException("Illegal image position value.");m_imagePos = pos;}// ------------------------------------------------------------------------/*** Get the horizontal alignment of this node with respect to its* x, y coordinates.* @return the horizontal alignment, one of* {@link prefuse.Constants#LEFT}, {@link prefuse.Constants#RIGHT}, or* {@link prefuse.Constants#CENTER}.*/public int getHorizontalAlignment() {return m_xAlign;}/*** Get the vertical alignment of this node with respect to its* x, y coordinates.* @return the vertical alignment, one of* {@link prefuse.Constants#TOP}, {@link prefuse.Constants#BOTTOM}, or* {@link prefuse.Constants#CENTER}.*/public int getVerticalAlignment() {return m_yAlign;}/*** Set the horizontal alignment of this node with respect to its* x, y coordinates.* @param align the horizontal alignment, one of* {@link prefuse.Constants#LEFT}, {@link prefuse.Constants#RIGHT}, or* {@link prefuse.Constants#CENTER}.*/ public void setHorizontalAlignment(int align) {m_xAlign = align;}/*** Set the vertical alignment of this node with respect to its* x, y coordinates.* @param align the vertical alignment, one of* {@link prefuse.Constants#TOP}, {@link prefuse.Constants#BOTTOM}, or* {@link prefuse.Constants#CENTER}.*/ public void setVerticalAlignment(int align) {m_yAlign = align;}/*** Returns the amount of padding in pixels between the content * and the border of this item along the horizontal dimension.* @return the horizontal padding*/public int getHorizontalPadding() {return m_horizBorder;}/*** Sets the amount of padding in pixels between the content * and the border of this item along the horizontal dimension.* @param xpad the horizontal padding to set*/public void setHorizontalPadding(int xpad) {m_horizBorder = xpad;}/*** Returns the amount of padding in pixels between the content * and the border of this item along the vertical dimension.* @return the vertical padding*/public int getVerticalPadding() {return m_vertBorder;}/*** Sets the amount of padding in pixels between the content * and the border of this item along the vertical dimension.* @param ypad the vertical padding*/public void setVerticalPadding(int ypad) {m_vertBorder = ypad;}/*** Get the padding, in pixels, between an image and text.* @return the padding between an image and text*/public int getImageTextPadding() {return m_imageMargin;}/*** Set the padding, in pixels, between an image and text.* @param pad the padding to use between an image and text*/public void setImageTextPadding(int pad) {m_imageMargin = pad;}} // end of class LabelRenderer

  从该类的红色代码可以发现,这里只有两种形状RoundRectangle2D和Rectangle2D,所以需要修改源码,添加第三种形状圆形即Arc2D。

  改动如下:

package prefuse.render;import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Arc2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RectangularShape;
import java.awt.geom.RoundRectangle2D;
import java.util.Iterator;import prefuse.Constants;
import prefuse.data.Node;
import prefuse.util.ColorLib;
import prefuse.util.FontLib;
import prefuse.util.GraphicsLib;
import prefuse.util.StringLib;
import prefuse.visual.NodeItem;
import prefuse.visual.VisualItem;/*** 渲染一个包含或文本或图片或兼有之的label标签 Renderer that draws a label, which consists of a* text string, an image, or both.* * <p>* When created using the default constructor, the renderer attempts to use text* from the "label" field. To use a different field, use the appropriate* constructor or use the {@link #setTextField(String)} method. To perform* custom String selection, subclass this Renderer and override the* {@link #getText(VisualItem)} method. When the text field is <code>null</code>* , no text label will be shown. Labels can span multiple lines of text,* determined by the presence of newline characters ('\n') within the text* string.* </p>* * <p>* By default, no image is shown. To show an image, the image field needs to be* set, either using the appropriate constructor or the* {@link #setImageField(String)} method. The value of the image field should be* a text string indicating the location of the image file to use. The string* should be either a URL, a file located on the current classpath, or a file on* the local filesystem. If found, the image will be managed internally by an* {@link ImageFactory} instance, which maintains a cache of loaded images.* </p>* * <p>* The position of the image relative to text can be set using the* {@link #setImagePosition(int)} method. Images can be placed to the left,* right, above, or below the text. The horizontal and vertical alignments of* either the text or the image can be set explicitly using the appropriate* methods of this class (e.g., {@link #setHorizontalTextAlignment(int)}). By* default, both the text and images are centered along both the horizontal and* vertical directions.* </p>* * @author <a href="http://jheer.org">jeffrey heer</a>*/
public class LabelRenderer extends AbstractShapeRenderer {  public static enum LabelShape{Rectangle2D,Arc2D}protected ImageFactory m_images = null;protected String m_delim = "\n";protected String m_labelName = "label";protected String m_imageName = null;protected int m_xAlign = Constants.CENTER;protected int m_yAlign = Constants.CENTER;protected int m_hTextAlign = Constants.CENTER;protected int m_vTextAlign = Constants.CENTER;protected int m_hImageAlign = Constants.CENTER;protected int m_vImageAlign = Constants.CENTER;protected int m_imagePos = Constants.LEFT;protected int m_horizBorder = 2;protected int m_vertBorder = 1;protected int m_imageMargin = 2;protected int m_arcWidth = 0;protected int m_arcHeight = 0;protected int m_maxTextWidth = -1;/** Transform used to scale and position images */AffineTransform m_transform = new AffineTransform();/** The holder for the currently computed bounding box */protected RectangularShape m_bbox = new Rectangle2D.Double(); // Prefuse框架自带的,即默认使用矩形protected Point2D m_pt = new Point2D.Double(); // temp pointprotected Font m_font; // temp font holderprotected String m_text; // label textprotected Dimension m_textDim = new Dimension(); // text width / height/*** Create a new LabelRenderer. By default the field "label" is used as the* field name for looking up text, and no image is used.*/public LabelRenderer() {}/*** Create a new LabelRenderer. Draws a text label using the given text data* field and does not draw an image.* * @param textField*            the data field for the text label.*/public LabelRenderer(String textField) {this.setTextField(textField);}/*** * @param textField* @param isRectangle*            true-矩形 false-圆形*/public LabelRenderer(String textField, LabelShape shape) {if (shape == LabelShape.Rectangle2D) {m_bbox = new Rectangle2D.Double();} else {m_bbox = new Arc2D.Double();}this.setTextField(textField);}/*** Create a new LabelRenderer. Draws a text label using the given text data* field, and draws the image at the location reported by the given image* data field.* * @param textField*            the data field for the text label* @param imageField*            the data field for the image location. This value in the data*            field should be a URL, a file within the current classpath, a*            file on the filesystem, or null for no image. If the*            <code>imageField</code> parameter is null, no images at all*            will be drawn.*/public LabelRenderer(String textField, String imageField) {setTextField(textField);setImageField(imageField);}// ------------------------------------------------------------------------/*** Rounds the corners of the bounding rectangle in which the text string is* rendered. This will only be seen if either the stroke or fill color is* non-transparent.* * @param arcWidth*            the width of the curved corner* @param arcHeight*            the height of the curved corner*/public void setRoundedCorner(int arcWidth, int arcHeight) {if ((arcWidth == 0 || arcHeight == 0) && !(m_bbox instanceof Rectangle2D)) {m_bbox = new Rectangle2D.Double();} else {if (!(m_bbox instanceof RoundRectangle2D))m_bbox = new RoundRectangle2D.Double();((RoundRectangle2D) m_bbox).setRoundRect(0, 0, 10, 10, arcWidth, arcHeight);m_arcWidth = arcWidth;m_arcHeight = arcHeight;}}/*** Get the field name to use for text labels.* * @return the data field for text labels, or null for no text*/public String getTextField() {return m_labelName;}/*** Set the field name to use for text labels.* * @param textField*            the data field for text labels, or null for no text*/public void setTextField(String textField) {m_labelName = textField;}/*** Sets the maximum width that should be allowed of the text label. A value* of -1 specifies no limit (this is the default).* * @param maxWidth*            the maximum width of the text or -1 for no limit*/public void setMaxTextWidth(int maxWidth) {m_maxTextWidth = maxWidth;}/*** Returns the text to draw. Subclasses can override this class to perform* custom text selection.* * @param item*            the item to represent as a <code>String</code>* @return a <code>String</code> to draw*/protected String getText(VisualItem item) {String s = null;if (item.canGetString(m_labelName)) {return item.getString(m_labelName);}return s;}// ------------------------------------------------------------------------// Image Handling/*** Get the data field for image locations. The value stored in the data* field should be a URL, a file within the current classpath, a file on the* filesystem, or null for no image.* * @return the data field for image locations, or null for no images*/public String getImageField() {return m_imageName;}/*** Set the data field for image locations. The value stored in the data* field should be a URL, a file within the current classpath, a file on the* filesystem, or null for no image. If the <code>imageField</code>* parameter is null, no images at all will be drawn.* * @param imageField*            the data field for image locations, or null for no images*/public void setImageField(String imageField) {if (imageField != null)m_images = new ImageFactory();m_imageName = imageField;}/*** Sets the maximum image dimensions, used to control scaling of loaded* images. This scaling is enforced immediately upon loading of the image.* * @param width*            the maximum width of images (-1 for no limit)* @param height*            the maximum height of images (-1 for no limit)*/public void setMaxImageDimensions(int width, int height) {if (m_images == null)m_images = new ImageFactory();m_images.setMaxImageDimensions(width, height);}/*** Returns a location string for the image to draw. Subclasses can override* this class to perform custom image selection beyond looking up the value* from a data field.* * @param item*            the item for which to select an image to draw* @return the location string for the image to use, or null for no image*/protected String getImageLocation(VisualItem item) {return item.canGetString(m_imageName) ? item.getString(m_imageName) : null;}/*** Get the image to include in the label for the given VisualItem.* * @param item*            the item to get an image for* @return the image for the item, or null for no image*/protected Image getImage(VisualItem item) {String imageLoc = getImageLocation(item);return (imageLoc == null ? null : m_images.getImage(imageLoc));}// ------------------------------------------------------------------------// Renderingprivate String computeTextDimensions(VisualItem item, String text, double size) {// put item font in temp member variablem_font = item.getFont();// scale the font as neededif (size != 1) {m_font = FontLib.getFont(m_font.getName(), m_font.getStyle(), size * m_font.getSize());}FontMetrics fm = DEFAULT_GRAPHICS.getFontMetrics(m_font);StringBuffer str = null;// compute the number of lines and the maximum widthint nlines = 1, w = 0, start = 0, end = text.indexOf(m_delim);m_textDim.width = 0;String line;for (; end >= 0; ++nlines) {w = fm.stringWidth(line = text.substring(start, end));// abbreviate line as neededif (m_maxTextWidth > -1 && w > m_maxTextWidth) {if (str == null)str = new StringBuffer(text.substring(0, start));str.append(StringLib.abbreviate(line, fm, m_maxTextWidth));str.append(m_delim);w = m_maxTextWidth;} else if (str != null) {str.append(line).append(m_delim);}// update maximum width and substring indicesm_textDim.width = Math.max(m_textDim.width, w);start = end + 1;end = text.indexOf(m_delim, start);}w = fm.stringWidth(line = text.substring(start));// abbreviate line as neededif (m_maxTextWidth > -1 && w > m_maxTextWidth) {if (str == null)str = new StringBuffer(text.substring(0, start));str.append(StringLib.abbreviate(line, fm, m_maxTextWidth));w = m_maxTextWidth;} else if (str != null) {str.append(line);}// update maximum widthm_textDim.width = Math.max(m_textDim.width, w);// compute the text heightm_textDim.height = fm.getHeight() * nlines;return str == null ? text : str.toString();}/*** @see prefuse.render.AbstractShapeRenderer#getRawShape(prefuse.visual.VisualItem)*/protected Shape getRawShape(VisualItem item) {m_text = getText(item);Image  img  = getImage(item);double size = item.getSize();// get image dimensionsdouble iw=0, ih=0;if ( img != null ) {ih = img.getHeight(null);iw = img.getWidth(null);    }// get text dimensionsint tw=0, th=0;if ( m_text != null ) {m_text = computeTextDimensions(item, m_text, size);th = m_textDim.height;tw = m_textDim.width;   }// get bounding box dimensionsdouble w=0, h=0;switch ( m_imagePos ) {case Constants.LEFT:case Constants.RIGHT:w = tw + size*(iw +2*m_horizBorder+ (tw>0 && iw>0 ? m_imageMargin : 0));h = Math.max(th, size*ih) + size*2*m_vertBorder;break;case Constants.TOP:case Constants.BOTTOM:w = Math.max(tw, size*iw) + size*2*m_horizBorder;h = th + size*(ih + 2*m_vertBorder+ (th>0 && ih>0 ? m_imageMargin : 0));break;default:throw new IllegalStateException("Unrecognized image alignment setting.");}// get the top-left point, using the current alignment settingsgetAlignedPoint(m_pt, item, w, h, m_xAlign, m_yAlign);if (m_bbox instanceof RoundRectangle2D) {// 圆角矩形RoundRectangle2D rr = (RoundRectangle2D) m_bbox;rr.setRoundRect(m_pt.getX(), m_pt.getY(), w, h, size * m_arcWidth, size * m_arcHeight);} else if (m_bbox instanceof Arc2D) {// 圆形Arc2D rr = (Arc2D) m_bbox;rr.setArc(m_pt.getX(), m_pt.getY(), w, w, 360, 360, Arc2D.OPEN);} else {m_bbox.setFrame(m_pt.getX(), m_pt.getY(), w, h);// 矩形} return m_bbox;}/*** Helper method, which calculates the top-left co-ordinate of an item given* the item's alignment.*/protected static void getAlignedPoint(Point2D p, VisualItem item, double w, double h, int xAlign, int yAlign) {double x = item.getX(), y = item.getY();if (Double.isNaN(x) || Double.isInfinite(x))x = 0; // safety checkif (Double.isNaN(y) || Double.isInfinite(y))y = 0; // safety checkif (xAlign == Constants.CENTER) {x = x - (w / 2);} else if (xAlign == Constants.RIGHT) {x = x - w;}if (yAlign == Constants.CENTER) {y = y - (h / 2);} else if (yAlign == Constants.BOTTOM) {y = y - h;}p.setLocation(x, y);}/*** @see prefuse.render.Renderer#render(java.awt.Graphics2D,*      prefuse.visual.VisualItem)*/public void render(Graphics2D g, VisualItem item) {RectangularShape shape = (RectangularShape) getShape(item);if (shape == null)return;// fill the shape, if requestedint type = getRenderType(item);if (type == RENDER_TYPE_FILL || type == RENDER_TYPE_DRAW_AND_FILL)GraphicsLib.paint(g, item, shape, getStroke(item), RENDER_TYPE_FILL);// now render the image and textString text = m_text;Image img = getImage(item);if (text == null && img == null)return;double size = item.getSize();boolean useInt = 1.5 > Math.max(g.getTransform().getScaleX(), g.getTransform().getScaleY());double x = shape.getMinX() + size * m_horizBorder;double y = shape.getMinY() + size * m_vertBorder;// render imageif (img != null) {double w = size * img.getWidth(null);double h = size * img.getHeight(null);double ix = x, iy = y;// determine one co-ordinate based on the image positionswitch (m_imagePos) {case Constants.LEFT:x += w + size * m_imageMargin;break;case Constants.RIGHT:ix = shape.getMaxX() - size * m_horizBorder - w;break;case Constants.TOP:y += h + size * m_imageMargin;break;case Constants.BOTTOM:iy = shape.getMaxY() - size * m_vertBorder - h;break;default:throw new IllegalStateException("Unrecognized image alignment setting.");}// determine the other coordinate based on image alignmentswitch (m_imagePos) {case Constants.LEFT:case Constants.RIGHT:// need to set image y-coordinateswitch (m_vImageAlign) {case Constants.TOP:break;case Constants.BOTTOM:iy = shape.getMaxY() - size * m_vertBorder - h;break;case Constants.CENTER:iy = shape.getCenterY() - h / 2;break;}break;case Constants.TOP:case Constants.BOTTOM:// need to set image x-coordinateswitch (m_hImageAlign) {case Constants.LEFT:break;case Constants.RIGHT:ix = shape.getMaxX() - size * m_horizBorder - w;break;case Constants.CENTER:ix = shape.getCenterX() - w / 2;break;}break;}if (useInt && size == 1.0) {// if possible, use integer precision// results in faster, flicker-free image renderingg.drawImage(img, (int) ix, (int) iy, null);} else {m_transform.setTransform(size, 0, 0, size, ix, iy);g.drawImage(img, m_transform, null);}}// render textint textColor = item.getTextColor();if (text != null && ColorLib.alpha(textColor) > 0) {g.setPaint(ColorLib.getColor(textColor));g.setFont(m_font);FontMetrics fm = DEFAULT_GRAPHICS.getFontMetrics(m_font);// compute available widthdouble tw;switch (m_imagePos) {case Constants.TOP:case Constants.BOTTOM:tw = shape.getWidth() - 2 * size * m_horizBorder;break;default:tw = m_textDim.width;}// compute available heightdouble th;switch (m_imagePos) {case Constants.LEFT:case Constants.RIGHT:th = shape.getHeight() - 2 * size * m_vertBorder;break;default:th = m_textDim.height;}// compute starting y-coordinatey += fm.getAscent();switch (m_vTextAlign) {case Constants.TOP:break;case Constants.BOTTOM:y += th - m_textDim.height;break;case Constants.CENTER:y += (th - m_textDim.height) / 2;}// render each line of textint lh = fm.getHeight(); // the line heightint start = 0, end = text.indexOf(m_delim);for (; end >= 0; y += lh) {drawString(g, fm, text.substring(start, end), useInt, x, y, tw);start = end + 1;end = text.indexOf(m_delim, start);}drawString(g, fm, text.substring(start), useInt, x, y, tw);}// draw borderif (type == RENDER_TYPE_DRAW || type == RENDER_TYPE_DRAW_AND_FILL) {GraphicsLib.paint(g, item, shape, getStroke(item), RENDER_TYPE_DRAW);}}private final void drawString(Graphics2D g, FontMetrics fm, String text, boolean useInt, double x, double y, double w) {// compute the x-coordinatedouble tx;switch (m_hTextAlign) {case Constants.LEFT:tx = x;break;case Constants.RIGHT:tx = x + w - fm.stringWidth(text);break;case Constants.CENTER:tx = x + (w - fm.stringWidth(text)) / 2;break;default:throw new IllegalStateException("Unrecognized text alignment setting.");}// use integer precision unless zoomed-in// results in more stable drawingif (useInt) {g.drawString(text, (int) tx, (int) y);} else {g.drawString(text, (float) tx, (float) y);}}/*** Returns the image factory used by this renderer.* * @return the image factory*/public ImageFactory getImageFactory() {if (m_images == null)m_images = new ImageFactory();return m_images;}/*** Sets the image factory used by this renderer.* * @param ifact*            the image factory*/public void setImageFactory(ImageFactory ifact) {m_images = ifact;}// ------------------------------------------------------------------------/*** Get the horizontal text alignment within the layout. One of* {@link prefuse.Constants#LEFT}, {@link prefuse.Constants#RIGHT}, or* {@link prefuse.Constants#CENTER}. The default is centered text.* * @return the horizontal text alignment*/public int getHorizontalTextAlignment() {return m_hTextAlign;}/*** Set the horizontal text alignment within the layout. One of* {@link prefuse.Constants#LEFT}, {@link prefuse.Constants#RIGHT}, or* {@link prefuse.Constants#CENTER}. The default is centered text.* * @param halign*            the desired horizontal text alignment*/public void setHorizontalTextAlignment(int halign) {if (halign != Constants.LEFT && halign != Constants.RIGHT && halign != Constants.CENTER)throw new IllegalArgumentException("Illegal horizontal text alignment value.");m_hTextAlign = halign;}/*** Get the vertical text alignment within the layout. One of* {@link prefuse.Constants#TOP}, {@link prefuse.Constants#BOTTOM}, or* {@link prefuse.Constants#CENTER}. The default is centered text.* * @return the vertical text alignment*/public int getVerticalTextAlignment() {return m_vTextAlign;}/*** Set the vertical text alignment within the layout. One of* {@link prefuse.Constants#TOP}, {@link prefuse.Constants#BOTTOM}, or* {@link prefuse.Constants#CENTER}. The default is centered text.* * @param valign*            the desired vertical text alignment*/public void setVerticalTextAlignment(int valign) {if (valign != Constants.TOP && valign != Constants.BOTTOM && valign != Constants.CENTER)throw new IllegalArgumentException("Illegal vertical text alignment value.");m_vTextAlign = valign;}/*** Get the horizontal image alignment within the layout. One of* {@link prefuse.Constants#LEFT}, {@link prefuse.Constants#RIGHT}, or* {@link prefuse.Constants#CENTER}. The default is a centered image.* * @return the horizontal image alignment*/public int getHorizontalImageAlignment() {return m_hImageAlign;}/*** Set the horizontal image alignment within the layout. One of* {@link prefuse.Constants#LEFT}, {@link prefuse.Constants#RIGHT}, or* {@link prefuse.Constants#CENTER}. The default is a centered image.* * @param halign*            the desired horizontal image alignment*/public void setHorizontalImageAlignment(int halign) {if (halign != Constants.LEFT && halign != Constants.RIGHT && halign != Constants.CENTER)throw new IllegalArgumentException("Illegal horizontal text alignment value.");m_hImageAlign = halign;}/*** Get the vertical image alignment within the layout. One of* {@link prefuse.Constants#TOP}, {@link prefuse.Constants#BOTTOM}, or* {@link prefuse.Constants#CENTER}. The default is a centered image.* * @return the vertical image alignment*/public int getVerticalImageAlignment() {return m_vImageAlign;}/*** Set the vertical image alignment within the layout. One of* {@link prefuse.Constants#TOP}, {@link prefuse.Constants#BOTTOM}, or* {@link prefuse.Constants#CENTER}. The default is a centered image.* * @param valign*            the desired vertical image alignment*/public void setVerticalImageAlignment(int valign) {if (valign != Constants.TOP && valign != Constants.BOTTOM && valign != Constants.CENTER)throw new IllegalArgumentException("Illegal vertical text alignment value.");m_vImageAlign = valign;}/*** Get the image position, determining where the image is placed with* respect to the text. One of {@link Constants#LEFT},* {@link Constants#RIGHT}, {@link Constants#TOP}, or* {@link Constants#BOTTOM}. The default is left.* * @return the image position*/public int getImagePosition() {return m_imagePos;}/*** Set the image position, determining where the image is placed with* respect to the text. One of {@link Constants#LEFT},* {@link Constants#RIGHT}, {@link Constants#TOP}, or* {@link Constants#BOTTOM}. The default is left.* * @param pos*            the desired image position*/public void setImagePosition(int pos) {if (pos != Constants.TOP && pos != Constants.BOTTOM && pos != Constants.LEFT && pos != Constants.RIGHT && pos != Constants.CENTER)throw new IllegalArgumentException("Illegal image position value.");m_imagePos = pos;}// ------------------------------------------------------------------------/*** Get the horizontal alignment of this node with respect to its x, y* coordinates.* * @return the horizontal alignment, one of {@link prefuse.Constants#LEFT},*         {@link prefuse.Constants#RIGHT}, or*         {@link prefuse.Constants#CENTER}.*/public int getHorizontalAlignment() {return m_xAlign;}/*** Get the vertical alignment of this node with respect to its x, y* coordinates.* * @return the vertical alignment, one of {@link prefuse.Constants#TOP},*         {@link prefuse.Constants#BOTTOM}, or*         {@link prefuse.Constants#CENTER}.*/public int getVerticalAlignment() {return m_yAlign;}/*** Set the horizontal alignment of this node with respect to its x, y* coordinates.* * @param align*            the horizontal alignment, one of*            {@link prefuse.Constants#LEFT},*            {@link prefuse.Constants#RIGHT}, or*            {@link prefuse.Constants#CENTER}.*/public void setHorizontalAlignment(int align) {m_xAlign = align;}/*** Set the vertical alignment of this node with respect to its x, y* coordinates.* * @param align*            the vertical alignment, one of {@link prefuse.Constants#TOP},*            {@link prefuse.Constants#BOTTOM}, or*            {@link prefuse.Constants#CENTER}.*/public void setVerticalAlignment(int align) {m_yAlign = align;}/*** Returns the amount of padding in pixels between the content and the* border of this item along the horizontal dimension.* * @return the horizontal padding*/public int getHorizontalPadding() {return m_horizBorder;}/*** Sets the amount of padding in pixels between the content and the border* of this item along the horizontal dimension.* * @param xpad*            the horizontal padding to set*/public void setHorizontalPadding(int xpad) {m_horizBorder = xpad;}/*** Returns the amount of padding in pixels between the content and the* border of this item along the vertical dimension.* * @return the vertical padding*/public int getVerticalPadding() {return m_vertBorder;}/*** Sets the amount of padding in pixels between the content and the border* of this item along the vertical dimension.* * @param ypad*            the vertical padding*/public void setVerticalPadding(int ypad) {m_vertBorder = ypad;}/*** Get the padding, in pixels, between an image and text.* * @return the padding between an image and text*/public int getImageTextPadding() {return m_imageMargin;}/*** Set the padding, in pixels, between an image and text.* * @param pad*            the padding to use between an image and text*/public void setImageTextPadding(int pad) {m_imageMargin = pad;}} // end of class LabelRenderer

    这样就可以完成如下的转变:

                                               

  (2)节点与标签无关联显示

  虽然(1)实现了图形的圆形节点显示,但是存在一个问题:节点的大小会因为节点上标签的不同而不同,显然这个节点大小是不具备任何意义的,所以,源码要改。我们要做的就是降低节点与标签的大小形状依赖,同时让节点的形状具有自己的意义,这里通过按照每个节点度数来决定节点的大小,为了提高健壮性,这里采用log函数对度数进行归一化处理,另外还对于节点添加边框显示。最终得到的结果如下:

  从以上两个例子,我们可以发现源码并不是不可撼动,源码随着时代的变迁也会有力不从心的那一天,

  3.三思而行——设计很重要 基础要扎实   

  (1)单例模式:

  在Prefuse讲求一个数据中心Visualization的概念,所有的约束和效果都会添加到Visualization中,那么如何保证在不同类中还能获得到具有相同配置参数的数据中心对象。我们知道,不同的对象会有不同的参数、成员,所以,这里我们采用单例模式。关于什么是单例模式想必大家都不陌生,有饿汉式和懒汉式,不清楚的,网上可以找资料了解下。采用单例模式的好处是,在任何地方我们只要创建某个类的对象都是同一对象,从而保证了该对象的一致性,比如在可视化工具中要向一个已有的数据可视化中心对象visualization中添加一个布局Action效果,如果不采用传值等方法而是新建一个对象,则会造成新建的数据中心对象中只有这个新添加的布局Action,从而"丢失"了前面的所有参数。

  该工具中的数据中心对象就是采用单例模式,部分代码如下:

private static Visualization visInstance;public static Visualization getVisualization(){if(visInstance == null){visInstance = new Visualization();}return visInstance;
}

  (2)匿名内部类:

  以前只是知道匿名内部类有其特有的优越性和方便性,简约,有其在Swing编程中经常被使用,但是一些使用细节不曾注意。

  在实际功能编写中,遇到类似于添加监听器这样的操作,经常出现在匿名内部类中的变量报错,提示信息也不清楚,后来网上一查,才知道原来在匿名内部类中使用的外部变量都需要为final类型,了解后对于匿名内部类与外部类之间的关系与生命周期又有了新的认识。关于匿名内部类与final的渊源详见这里。

  4.工具概览

  下面从一个图形的导入开始,通过图形大致了解工具的一些功能:

  (1)点击"打开..."菜单项后

  

  (2)选择socialnet.xml并点击'"打开"

  (3)点击"确定",图形展示

  

  可以看到左边信息提示面板显示了图的节点和边的数量以及图的性质,同时将鼠标放在节点"Jeff"上,会有高亮显示和近邻节点显示

  (4)点击"FruchtermanReingold布局"

  

  (5)点击标签设置中的"不显示"

  (6)点击节点设置中的"矩形"

(7)数据量稍大时展现不吃力

  虽然只是一个简单的工具,虽然这个工具还不成气候,虽然还有太多的体会没有及时提及,但是却真真实实的检验了自己也清醒的认识了自己。

  作为一名码农,应该恪守"知行合一"的铁律,没做整合之前不要说自己能做到如何狂拽炫酷,也不用说自己如何举步维艰。做好可行性分析,踏踏实实写好每一行代码,不断思考是否有新的思路和更高效的设计和方法才是王道。

  如果觉得有用,记得点赞哦^_^

  本文链接:《漫谈可视化Prefuse(五)---一款属于我自己的可视化工具》

  如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!如果您想持续关注我的文章,请扫描二维码,关注JackieZheng的微信公众号,我会将我的文章推送给您,并和您一起分享我日常阅读过的优质文章。

  

友情赞助

如果你觉得博主的文章对你那么一点小帮助,恰巧你又有想打赏博主的小冲动,那么事不宜迟,赶紧扫一扫,小额地赞助下,攒个奶粉钱,也是让博主有动力继续努力,写出更好的文章^^。

    1. 支付宝                          2. 微信

                      

转载于:https://www.cnblogs.com/bigdataZJ/p/VisualizationSoloShow5.html

漫谈可视化Prefuse(五)---一款属于我自己的可视化工具相关推荐

  1. 一款好用的数据可视化报表分析工具

    随着信息化建设的快速发展,可视化需求越来越高,很多企业随着数据可视化越来越受到推崇,对数据可视化的要求也越来越高.而且在大数据时代来临后,大屏显示系统也不再单单作为显示工具,只是将图像.数据信号传输到 ...

  2. 漫谈可视化Prefuse(四)---被玩坏的Prefuse API

    这个双12,别人都在抢红包.逛淘宝.上京东,我选择再续我的"漫谈可视化"系列(好了,不装了,其实是郎中羞涩...) 上篇<漫谈可视化Prefuse(三)---Prefuse ...

  3. 5款免费的优秀数据可视化工具推荐

    数据可视化软件是做什么用的? 如果你曾经用过PPT,就会发现其中可以添加各种各样的图表.图片.视频等组件.这些组件的目的是将常人难以理解.接受的数据以可视化的形式表现出来,让人们能够更加直观地感受到数 ...

  4. 五款移动APP在线原型设计工具,值得收藏

    今天已到2014年的最后一天了,25学堂还是一无既往的为大家提供最优质最简单通俗的移动APP设计资源和移动APP设计干货. 25学堂整理了国内外4个比较操作易上手的移动APP在线原型设计工具推荐给大家 ...

  5. 22 款神经网络的设计和可视化工具

    前言 深度学习领域,最常见的就是各种网络模型,那么在写论文或者文章,介绍网络模型的时候,最好的办法当然就是展示代码画图,今天介绍的 Github 项目,就是整理了 22 个设计和可视化网络结构的工具, ...

  6. colorpix取色小工具_五款互联网人必备的免费工具,超级实用

    运营公举小磊磊(公众号ID:gongjulei)所有文章均为独家原创,未经许可,不得转载. 一名互联网人,从来都不会嫌工具多.小公举今天找到了5款互联网人必备的免费工具,都是我亲自试用过的.话不多说, ...

  7. 推荐14款基于javascript的数据可视化工具

    2019独角兽企业重金招聘Python工程师标准>>> 随着数据可视化概念逐年火热,有较多优秀的图表开源库和制作工具脱颖而出,下面,我们就拿其中比较有名的 14个产品进行简要介绍. ...

  8. 想做价值数万的可视化图表?这款免费软件不能错过!

    都说数据可视化是数据分析的最后一公里,这年头,会数据可视在互联网到底有多吃香?小编这就来给大家科普一下,曾经在某招聘软件上看到字节跳动.腾讯等大厂在招聘要求里面明确写明应聘者需要掌握数据可视化,开出月 ...

  9. 推荐一款制作H5页面的可视化工具

    推荐一款制作H5页面的可视化工具 无需写代码,可视化H5页面制作编辑器.使用JS写的,直接谷歌浏览器打开即可使用. 无需写代码,可视化H5页面制作编辑器.使用JS写的,直接谷歌浏览器打开即可使用. 下 ...

  10. 苹果电脑上好用的五款文档文字编辑处理工具

    苹果电脑上文字编辑必不可少,那么哪个更好用呢,今天就为大家分享几款苹果电脑上好用的五款文档文字编辑处理工具,以做参考. 第一款:word 2019中文 文档处理软件中 ,我们常用的也就两个:一是off ...

最新文章

  1. 第九届全国大学生智能汽车竞赛获奖名单
  2. LeetCode 1 两数之和
  3. 用python绘制漂亮的图形-用Python画一些漂亮图形--Quora代码赏析
  4. 如何快速在Linux系统的硬盘上创建大文件
  5. Redis常见问题及其一些重点知识总结
  6. 封装类(Merry May Day to all you who are burried in work ~)---2017-05-01
  7. 吴恩达《机器学习》第十三章:聚类
  8. 超级计算机的容量单位,微型计算机内存容量的基本单位(计算机内存容量什么单位)...
  9. 手把手带你免费申请《软件著作权》 超详细计算机软件著作权申请教程 文末送模板
  10. 计算机网络原理-计算题汇总
  11. 昂达v80 plus linux,8英寸便携平板 昂达V80 Plus一体工艺来袭
  12. 三角形顺时针逆时针判断
  13. 阿里云服务器接入云盾Web应用防火墙教程
  14. JavaScript系列文章:变量提升和函数提升
  15. scratch——这个电路仿真工具真不错
  16. java枚举类构造方法默认是,枚举类型的构造方法_Java语言程
  17. 51 单片机实战教程(13 外围芯片驱动程序之CS1237芯片驱动)
  18. 单个路由器设置计算机无线网络,无线路由器无线MAC地址过滤设置方法(指定电脑上网)...
  19. HttpSession的常见用法(javaWeb)
  20. 希赛免费试看教学视频

热门文章

  1. Asp中显示%百分比函数FormatPercent的使用
  2. 搭建frp进行内网穿透
  3. Java实现简易图形编辑系统
  4. Visual Studio Code——做嵌入式C/C++开发常用的编辑器软件安装及基本使用总结
  5. 鸿蒙大陆v2.8正式版,鸿蒙大陆正式版地图下载-鸿蒙大陆下载 V2.22--pc6下载站
  6. Python OpenCV 在视频上添加文字后保存视频
  7. 国外设计素材网址干货
  8. 计算机上的闹钟怎么设置方法,pconline,教您电脑闹钟怎么设置
  9. MATLAB椎体怎么画,求以半径为R的圆为底,平行且等于底圆直径的线段为顶,高为h的正劈锥体的体积.用matlab作!急用...
  10. 【ICCV2019论文阅读】PU-GAN:点云上采样对抗网络