海水正蓝

面朝大海,春暖花开
posts - 145, comments - 29, trackbacks - 0, articles - 1
  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

图形上下文导论(Introduction to SWT Graphics)

摘要:

org.eclipse.swt.graphics包(package),包含了管理图形资源的类。只要实现了org.eclipse.swt.graphics.Drawable接口,就可在上绘画包括 org.eclipse.swt.widgets.Controlorg.eclipse.swt.graphics.Imageorg.eclipse.swt.graphics.GC封装了全部绘画API,包括如何绘制线条、图形、如何绘制文本、图像以及填充图形。 本篇展示如何使用GC在图像上绘画, 控件的绘画事件(paintEvent)回调。画布(Canvas)控件因为不同的绘画操作,拥有很多构造风格常量允许你指定何时以及如何产生绘画本篇将展示这些东西。

英文原文:Graphics Context - Quick on the draw   http://www.eclipse.org/articles/Article-SWT-graphics/SWT_graphics.html

作者: Joe Winchester, IBM
2003年7月3日(July 3, 2003)
催月泪(Jaclick)翻译  Jaclick@gmail.com
2007.5.30
Copyright 2003 International Business Machines Corp.
目录:

 

SWT图形系统使用了与控件(Control)相同的坐标习惯,即客户区最左上角开始的点为原点(0,0),x轴坐标向右增加y轴坐标向下增加。Point 类被用于描述位置(坐标系统中的位置)以及偏移量(SWT中并没有Dimension类,矩形(rectangle)的大小是由Point类捕获的x、y坐标点偏移量到原点坐标来描述的)。

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">图形上下文(Graphics Context)

图形能够被画在任何实现了org.eclipse.swt.graphics.Drawable接口的东西上,比如控件(Control)、图像(Image)、显示设备或者打印设备。 org.eclipse.swt.graphics.GC 就是封装了执行绘画操作的图形上下文(graphics context)。一般有两种使用GC的方法:一种是Drawable 实例作为GC构造函数的参数获取的GC,另一种是绘画事件(paintEvent)回调提供的GC。

在图像(Image)上绘画

以下代码是把一个图像作为构造参数获取图像的GC,然后在它上面绘制线条。 从左上角顶点(0,0)处右下角顶点画线条  从右上角顶点向左下角顶点画线条。

   

    Image image = new Image(display,"C:/devEclipse_02/eclipse/plugins/org.eclipse.platform_2.0.2/eclipse_lg.gif");
    GC gc = new GC(image);
    Rectangle bounds = image.getBounds(); 
 gc.drawLine(0,0,bounds.width,bounds.height); 
 gc.drawLine(0,bounds.height,bounds.width,0); 
 gc.dispose();
    image.dispose(); 


 原始图像 绘制线条后的图像

 

 由你创建的GC,就得由你负责销毁它。调用 dispose()方法。关于怎样管理 SWT 资源的更多信息请参见 SWT: The Standard Widget Toolkit 。一个 GC 实例应该在使用完后就尽可能快的释放,这是因为每一个GC 都需要占用底层系统平台资源,而在某些操作系统平台中,这些资源是相当匮乏的,例如Windows 98仅仅提供了5个GC 对象。

在控件(Control)上绘画

org.eclipse.swt.widgets.Control 实现了Drawable接口,所以你可以在控件(Control)上绘画,图像(Image)上绘画方法与控件相同(把控件或图像作为参数传给GC获取控件或图像的GC,然后在其上进行绘画)。但是,图像(Image)上绘画与控件有所不同图像的修改是永恒不变的。如果使用GC在控件上进行绘画,操作系统自身在绘制控件时会覆盖你所做的绘画操作。正确的方法的是为控件添加一个绘画监听器,这个监听器类就是org.eclipse.swt.events.PaintListener,然后在监听器中回调方法参数就是org.eclipse.swt.events.PaintEvent的一个实例PaintEvent 包含了一GC,这样在控件上面或者是指定区域里面进行绘画的环境就准备好了

 

以下代码 给Shell添加了一个绘画监听, 然后在paintControl()方法中画一条连接原点到底部右下角的直线。

 

    Shell shell = new Shell(display); 
 shell.addPaintListener(new PaintListener(){
        public void paintControl(PaintEvent e){
            Rectangle clientArea = shell.getClientArea(); 
         e.gc.drawLine(0,0,clientArea.width,clientArea.height);
        }
    });
    shell.setSize(150,150)

 

  

虽然Shell的大小设置为(150,150), 但实际上可绘画的区域比这还要再小一些,因为Shell还包括了边框、工具栏以及菜单栏这也就是我们所要了解的客户区域。任何面板(Composite)都是使用getClientArea()方法获取客户区域的

因为应用程序总是在底层OS绘制完控件后才得到绘画事件,所以绘画事件中的GC进行绘画后的效果就可以最终显示在控件上面了。当然也有例外,比如工具栏区域就不能在上面进行绘画。org.eclipse.swt.widgets.Canvas 能够用来进行多方面的图形绘画操作。

 

剪切(Clipping)

 

一个GC的剪切区域就是发生绘画的那部分,这里有个例子,如果你要填充出一个有缺口的三角形形状,一种方法是画出多个三角形和矩形组合出这么一个形状;当然也有另一种方法,就是利用GC的剪切操作。

   

    shell.addPaintListener(new PaintListener() {
        public void paintControl(PaintEvent e) {
            Rectangle clientArea = shell.getClientArea();
            int width = clientArea.width;
            int height = clientArea.height; 
         e.gc.setClipping(20,20,width - 40, height - 40);
            e.gc.setBackground(display.getSystemColor(SWT.COLOR_CYAN)); 
         e.gc.fillPolygon(new int[] {0,0,width,0,width/2,height});
        }
    });

 

这段代码在Shell上画了一个三角形。 左上角和右上角连接到底部边缘的中间。使用一个矩形CG对其进行剪切。 最后显示出被剪切的矩形区域。 

 

 

当控件发生绘画事件,GC总是剪切需要重绘的那部分区域。例如,另一个窗口移到了一个SWT shell的前面,随后又移走。那么需要重新显示的就是GUI被损坏的那部分(shell被覆盖的那部分),一个绘画事件就是事件队列。当绘画事件发生,paintControl(PaintEvent evt) 方法中的参数就包含了控件中需要的重绘区域的x、y坐标字段及宽和高字段。控件的受损部分能够包含若干个相分离的矩形区域,当绘画事件发生,控件的受损部 分不止一个时,那么它们就会被合并成一个单一的矩形。这一步是由底层平台来实现的,因为多个绘画事件在单独的一个回调过程中处理有利于执行。

 

在上面的例子中每当 paintControl(PaintEvent)被调用的时候, 就将在PaintEvent's area中寻找一个优化绘画事件(paint event)很可能不交叉在绘画的形状(shape)中,在这种情况下,就不需要绘画(painting)或者指使需要一部分重画而已依靠绘画的类型,就可以解决GC所选择的绘画部分,但事实上这要比GC剪切花费更多开销,而且在实践中常常忽视这些被损坏的区域让GC重新绘画全部,只有在刷新操作中才会依赖剪切。

 

如果程序需要手工损坏控件的某部分区域,可以使用Control.redraw(int x, int y, int width, int height)或者使用Control.redraw()损坏整个客户区域。此区域就被打上了标记然后包含在下一个绘画事件中,产生闪屏后,就会立即使用Control.update()方法强制处理控件的绘画请求。如果无绘画请求(也就是客户区域无损坏), update()就什么不做。

(译者注:此处的Control并不单指Control类,而是指所有继承了Control类的控件类,比如button,canvas,shell等等)

画布(Canvas)

虽然任何控件都可以通过绘画事件(paintEvent)在其上进行绘制, 但是org.eclipse.swt.widgets.Canvas 是针对图形操作而特别设计的。可以直接使用Canvas,也可以通过添加绘画事件(paintEvent)使用,还可以创建Canvas的子类来自定义控件重复使用之。画布(Canvas)拥有大量的风格样式来影响绘画的产生。 

 

Canvas的默认行为是使用当前背景色填充自身的整个客户区域。这样会引起屏闪,因为绘画事件也是在GC上绘画,所以用户就会看到被填充的原始背 景色和产生绘画之间的闪烁。有一种方法可避免此类情况,在创建Canvas时使用SWT.NO_BACKGROUND样式。这样就防止了绘画背景,意思就 是程序要负责绘画客户区域的每一个像素。

 

当部件调整大小时,客户区域会重复绘画,这就会出现屏幕闪烁。使用SWT.NO_REDRAW_RESIZE 可减少这样的情况,控件会减少不必要的重绘。比如改变尺寸大小,绘画事件GC只会剪切需要重绘的部分即底部区域和右边区域,就像一个反方向的“L”。

 

在固定大小的GC上绘画NO-REDRAW_RESIZE样式能很好的减少屏闪。但是错误的使用NO_REDRAW_RESIZE 可以导致图形成扁圆形。扁圆形是个大概的说法,事实上是指部件没有随大小的调整而进行正确的更新。下面的例子就演示了这样的情况。 填充椭圆形。因为在窗口大小改变时没有产生绘画事件,因为GC只剪切受损的(发生改变的)区域,而上一个绘画又没有被抹去,这就产生了扁圆形状。( 即使用NO_REDRAW_RESIZE 绘画事件只处理扩大的那部分区域,原先部分它就不管了).

 

    shell.setLayout(new FillLayout()); 
 final Canvas canvas = new Canvas(shell,SWT.NO_REDRAW_RESIZE);

    canvas.addPaintListener(new PaintListener() {
        public void paintControl(PaintEvent e) {
            Rectangle clientArea = canvas.getClientArea();
            e.gc.setBackground(display.getSystemColor(SWT.COLOR_CYAN)); 
         e.gc.fillOval(0,0,clientArea.width,clientArea.height);
        }
    });

 

canvas的大小被增大,GC只剪切需要重绘的地方,扁圆形状就产生了。

 

 

问题出在 ,应该使用SWT.NONE 样式,这样GC就不会只剪切扩大的部分了。所以当Shell大小增大时整个椭圆形都会被重绘。

 

 final Canvas canvas = new Canvas(shell,SWT.NONE);

 

任何SWT部件,如果超过一个矩形区域被“损坏”,平台会把它们合并成一个,也就是说SWT程序只能处理一个绘画事件。在Canvas上使用NO_MERGE_PAINTS 样式可以覆盖这样的行为,可以为每一个被“损坏”的矩形区域调用绘画事件监听。

 

风格常量NO_BACKGROUND, NO_REDRAW_RESIZE 以及NO_MERGE_PAINTS 能够被使用在任何面板(Composite)以及子类中, 包括Canvas、Shell以及Group。 虽然这是被SWT允许的(不会由异常抛出),但在Composite 类的Javadoc中关于风格有这样的警告 "... 如果在其他的Composite子类中(除了Canvas)使用其行为是不明确的。"。所以实现图形绘画操作Canvas应该是首选。

 

另一种减少屏幕闪烁的方法,就是使用双缓冲技术。你可以先根据Canvas客户区域大小创建Image对象,然后使用GC(Image)将其绘画到Canvas上;  在绘画事件GC中调用drawImage(Image image, int x, int y)。 在一些平台上已经为你实现了双缓冲,所以你可以根据情况考虑使用三缓冲。" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">

 

绘制线条和形状(Drawing lines and shapes)

GC 拥有很多绘画线条的方法,比如画连接两个坐标点的直线、连接多个坐标点的直线或者是预先定义好的形状,线条颜色就是GC的前景色,可以通过风格样式常量来 决定线条的粗细胖瘦。绘画事件其GC也有很多相同的属性(前景色、背景色、以及颜色),并且线条的默认宽度是1像素。

GC.drawLine(int x1, int y1, int x2, int y2);

画一条从坐标点(x1,y1)到坐标点(x2,y2)的直线,如果两点的坐标值相同就相当于画一个圆点。

GC.drawPolyline(int[] pointArray);

画一条连接多个坐标点的直线,int[] 存放着要连接的x、y坐标值。代码如下:

 

gc.drawPolyline(new int[] { 25,5,45,45,5,45 });

先是从坐标点25,5到45,45,然后从45,45到5,45。

 

GC.drawPolygon(int[] pointArray);

drawPolyline(int[])的使用与gc.drawPolyline很相似,不同的是最后一个点(5,45)连接了第一个点(25,5)。

 

gc.drawPolygon(new int[] { 25,5,45,45,5,45 });

相当于用三条线段链接三角形的单个端点,从而形成了一个三角形区域。

GC.drawRectangle(int x, int y, int width, int height);

画一个矩形区域,int x,int y是矩形左上角的坐标点,int width,int heighy分别是矩形的宽和高。

 

gc.drawRectangle(5,5,90,45);

 

左上角坐标点为(5,5),对角坐标点为(95,50)。

 

你可以将Rectangle作为一个单独的参数传送给绘画方法。GC.drawRectangle(Rectangle);

GC.drawRoundedRectangle(int x, int y, int width, int height, int arcWidth, int arcHeight);

圆角矩形不同于标准矩形,它的四角呈圆形。每一个圆角实际上就是一个1/4的椭圆形,其弧宽和弧高就是完整椭圆形的宽和高。

 

gc.drawRoundedRectangle(5,5,90,45,25,15)

 

画了一个圆角矩形,左顶角坐标值(5,5)。下面是圆角矩形的右下角放大图,其宽和高分别是25、15。 

 

虽然调用4次drawArc()和4次drawLine()完全可以实现一个圆角矩形。但在一些平台下,例如Windows或者Photon,SWT就可以使用非常优秀的平台API。

GC.drawOval(int x, int y, int width, int height);

 

一个椭圆形是画在矩形里的,所以由矩形的左顶点坐标以及宽和高来定义。定义一个正圆形同样使用这个方法。

gc.drawOval(5,5,90,45);

 

GC.drawArc(int x, int y, int width, int height, int startAngle, int endAngle);

一个弧形是被画在一个指明了高和宽以及左顶角x,y坐标的矩形区域内。int startAngle是个角度,是开始画弧形的位置,开始点就是此角度与X轴坐标线相交的那个点。int endAngle同样是角度,它是弧形结束的位置,道理和int startAngle相同。

 

gc.drawArc(5,5,90,45,90,200);

 

这里画了一个弧形,从90度垂线和X轴坐标相交处开始,然后持续画200度。

 

GC.setLineStyle(int style);

线条(Lines)拥有多种风格,org.eclipse.swt.SWT中提供了定义线条风格的常量,这些线条风格常量以LINE_开头。 

SWT.LINE_SOLID
SWT.LINE_DOT
SWT.LINE_DASH
SWT.LINE_DASHDOT
SWT.LINE_DASHDOTDOT

GC.setLineWidth(int width);

线条的默认宽度是1像素(pixel), 当然也可以使用GC的lineWidth字段设置线条宽。

gc.setLineWidth(2);
gc.setLineWidth(4);

 

 

因为线条风格影响着所有的绘画操作,所以这也就使你可以画出不同边线风格的矩形或椭圆等图形。

 

gc.setLineWidth(3);
gc.drawOval(5,5,40,40);
gc.setLineWidth(1);
gc.setLineStyle(SWT.LINE_DOT);
gc.setForeground(display.getSystemColor(SWT.COLOR_BLUE));
gc.drawRectangle(60,5,60,40);

 


当GC的属性被改变,比如像线条的宽度、线条的风格或者是颜色,这些变化都会影响到后续的绘画操作。以上代码片段中,首先设置线条的宽度为3画了一个椭圆,随后重新设置线条属性画了一个边线是虚线的矩形。在SWT图形编程中,忘记重新设置这些字段属性的值是经常会犯的错误。

 

绘制文本(Drawing text)

 

GC之上同样可以绘制文本,文字的轮廓可通过GC的foreground color和font确定。你需要定义它的左上角坐标(就是字体的位置)以及字体的高和宽。绘制文本有两种设置方法:第一种是在drawText()绘制 方法里直接输入文本它将处理行分隔符和tabs制表符,常用来模仿一个Label。第二种是在drawString()绘制方法中输入字符串,没有tab 以及回车处理,常用于更加复杂的控件,就像StyledText常用于Eclipse Java editor那样。

GC.drawText(String text, int x, int y);

Font font = new Font(display,"Arial",14,SWT.BOLD | SWT.ITALIC);
// ...
gc.drawText("Hello World",5,5);
gc.setForeground(display.getSystemColor(SWT.COLOR_BLUE));
gc.setFont(font);
gc.drawText("Hello\tThere\nWide\tWorld",5,25);
// ...
font.dispose();

 

 

 

 

drawText API 支持控制转义字符,\t 就是tab\n就是回车换行。  

GC.drawString(String text, int x, int y);

Font font = new Font(display,"Arial",14,SWT.BOLD | SWT.ITALIC);
// ...
gc.drawString("Hello World",5,5);
gc.setForeground(display.getSystemColor(SWT.COLOR_BLUE));
gc.setFont(font);
gc.drawString("Hello\tThere\nWide\tWorld",5,25);
// ...
font.dispose()

 


使用drawString时,tab 和回车换行转义字符没有被处理。在GC绘制时字符串所占的大小基于它的内容和GC所设置的字体。获取字符串所占宽度可以分别使用GC.stringExtent(String text)和GC.textExtent(String text)这两个方法。 它们所返回的Point的x,y坐标值分别就是宽和高。

 

GC.drawText(String text, int x, int y, boolean isTransparent);

 

drawText(String text, int x, int y)绘制的文本使用的是GC的当前foreground color。当你希望文本透过背景色在最顶层显示的画,你可设置它的isTransparent这个布尔型的参数为true。此方法在图像(image) 上绘制时特别有用。

 

Font font = new Font(display,"Arial",12,SWT.BOLD | SWT.ITALIC);
Image image = new Image(display,"C:/devEclipse_02/eclipse/plugins/org.eclipse.platform_2.0.2/eclipse_lg.gif");
GC gc = new GC(image);
gc.drawText("Hello World",5,5);
gc.setFont(font);
gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE));
gc.drawText("Hello World",5,25,true);
gc.dispose();
image.dispose();
font.dispose();

 


 

GC.drawText(String text, int x, int y, int flags);

int flags指的是SWT.DRAW_DELIMITER, SWT.DRAW_TAB, SWT.DRAW_TRANSPARENT 以及 SWT.DRAW_MNEMONIC 样式常量。这些常量用来确定是否处理 \n , \t , 是否使用背景色透明,是否处理 &。

 

gc.drawImage(image,0,0);
gc.drawText("Hello\t&There\nWide\tWorld",5,5,SWT.DRAW_TRANSPARENT);
gc.drawText("Hello\t&There\nWide\tWorld",5,25,SWT.DRAW_DELIMITER | SWT.DRAW_TAB | SWT.DRAW_MNEMONIC );

 

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">填充形状(Filling shapes)

使用GC的foreground color画线条(边线), 使用GC的background color填充形状。

GC.fillPolygon(int[]);

gc.setBackground(display.getSystemColor(SWT.COLOR_BLUE));
gc.fillPolygon(new int[] { 25,5,45,45,5,45 })

GC.fillRectangle(int x, int y, int width, int height);

gc.fillRectangle(5,5,90,45);

 

 

 

填充矩形的时候,底部边线和右边线是不包含在内的。虽然点(5,5)被包含在填充矩形的代码中,但右下角点 95,50 (5+90 , 45+5) 不在填充区域的范围里,右下角的填充点是94,49。这不同于drawRectangle(5,5,90,45),drawRectangle指的是整个形状,所以其右下角点是95,50。

 

举例说明一下,下面的代码填充了一个矩形,但是填充的颜色并没有覆盖边线。填充区域的右上角点坐标以及宽和高都减小了1像素。

gc.drawRectangle(5,5,90,45);
gc.setBackground(display.getSystemColor(SWT.COLOR_CYAN));
gc.fillRectangle(6,6,89,44);

 

 

GC.fillRoundedRectangle(int x, int y, int width, int height, int arcWidth, int arcHeight);

gc.fillRoundRectangle(5,5,90,45,25,15);

 

 

有点像 GC.fillRectangle(...)方法。底部边框和右边框都被排除在填充范围之内,所以底部右下角坐标变成了(9449)而不是(9550)

 

GC.fillOval(int x, int y, int width, int height);

 

gc.fillOval(5,5,90,45);

 

 

与其他的填充APIs相似

 

GC.fillArc(int x, int y, int widt4h., int height, int startAngle, int endAngle);

 

gc.fillArc(5,5,90,45,90,200);

 


fillArc(...) 方法中的参数和drawArc(...)中的参数很相似。fillArc(...)遵守着和其他填充方法一样的模式,底部边框和右边框不在填充范围之内。 

 

GC.fillGradientRectangle(int x, int y, int width. int height, vertical boolean);

 

对矩形进行由前景色到背景色的渐变填充。Verticaltrue表示垂直渐变,反之则表示水平渐变。

 

gc.setBackgrouind(display,getSystemColor(SWT.COLOR_BLUE));
gc.fillGradientRectangle(5,5,90,45,false);

 

水平渐变从左边的黑色前景色开始向右边蓝色背景色变化。正如其他的填充方法,底部和右边框是被排除在外的,所以底部右下角会由1像素插入。 

 

gc.setBackground(display.getSystemColor(SWT.COLOR_BLUE));
gc.setForeground(display.getSystemColor(SWT.COLOR_CYAN));
gc.fillGradientRectangle(5,5,90,45,true);

 

垂直渐变从上而下,由前景色向背景色变化。 

异或(XOR)

GC的绘画发生时你可以在绘画表面上编辑图形的像素值,设置GC的异或(XOR)模式为true,每种颜色都是三原色红、绿、蓝经过异或(XOR)操作后的结果,那么你就可以将几种颜色经过异或(XOR)操作后得到新的颜色。

 

shell.setBackground(display.getSystemColor(SWT.COLOR_WHITE));
// ...
gc.setBackground(display.getSystemColor(SWT.COLOR_BLUE));
gc.fillRectangle(5,5,90,45);
gc.setXORMode(true);
gc.setBackground(display.getSystemColor(SWT.COLOR_WHITE));
gc.fillRectangle(20,20,50,50);
gc.setBackground(display.getSystemColor(SWT.COLOR_RED));
gc.fillOval(80,20,50,50);

 

 

被填充的背景色是白色的(255,255,255)矩形,当在上面覆盖一层蓝色(0,0,255),或(XOR)后的颜色就是黄色(25,255,0)。白背景的部分和黄色异或后就成了黑色(0,0,0)。一个红色背景的圆,它覆盖在蓝色上面异或(XOR)后就成了紫色(255,0,255)。盖在白色上面异或(XOR)后就成了青色(0,255,255)。

 

(译者注: SWT API帮助文档中对setXORMode()方法是这样描述的“此方法在某些平台下是不被支持的,显著表现的有Mac OS X,如果你希望你的代码可运行在所有平台,应该尽量避免使用此方法。”)

" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">绘制图像(Drawing Images)

org.eclipse.swt.graphics.Image 表示一个已经准备好在显示设备或打印设备上显示的图像。创建一个image对象最简单的方式就是从经过验证的文件格式中加载文件。支持的文件格式有 GIF, BMP (Windows 位图), JPG, PNG, 新的Eclipse releases版还支持TIFF格式。

Image image = new Image(display,"C:/eclipse/eclipse/plugins/org.eclipse.platform_2.0.2/eclipse_lg.gif");

GC.drawImage(Image image, int x, int y);

每一个图像(image)都有一个由它自身范围所决定的大小。例如图像eclipse_lg.gif的大小就是115,164 ,可以使用image.getBounds()获取。当绘画了一个图像,此图像就会以它自身范围的宽度和高度显示出来。

 

gc.drawImage(image,5,5);

GC.drawImage(Image image, int srcX, int srcY, int srcWidth, int srcHeight, int dstX, int dstY, int dstWidth, int dstHeight);

根据原始图像的宽度和高度,不但可以绘画出不同大小的图像还可以只绘画原始图像的局部。

src 参数联系图像本身,要画完整的图像,srcXsrcY 使用0,0,宽高就使用图像的宽高。dst 参数表示图像被画在哪里以及画成多大。原始图像的大小是115,164,若要把图像宽度增加2倍,高度减低一半,可以使用下面的语句:

 

gc.drawImage(image,0,0,115,164,5,5,230,82);


 

 

使用src坐标可以使你只画出图像的局部。例如,如果你只想画出图像的右上角部分,你可以设定src坐标为20,0,宽度和高度为9582。下面的代码中dst 宽度和高度同样使用95,82。通过指定不同大小就可以对图像进行拉长或收缩操作。

gc.drawImage(image,20,0,95,82,5,5,95,82);

 

 

还能完成一些其他的图像效果,比如图像透明度、animation以及alpha通道。但这些不属于本篇的讨论范围,我希望在以后的文章中能够涉及到这些东西。

总结(Conclusion)

文已经展示了如何使用GC画线条、文本和填充形状。给构造器传入一个drawable参数就能够创建其GC,例如一个图像、或者给控件(control使用绘画事件(paintEvent)回调。 GC 的API允许设置前景色绘画出线条并使用背景色填充。Canvas 控件允许通过绘画事件paintEvent绘画,并且当绘画事件发生时它有很多的构造常量可以使用。GC 的剪切操作允许你控制只想显示的部分,还有如何绘画出不同风格的线条以及显示文本和图像。

posted @ 2012-07-10 11:50 小胡子 阅读(451) | 评论 (0)编辑 收藏

    恐怕现在用过电脑的人,一定都知道大部分带文本编辑功能的软件都有一个快捷键ctrl+f 吧(比如word)。这个功能主要来完成“查找”,“替换”和“全部替换”功能的,其实这就是典型的模式匹配的应用,即在文本文件中查找串。

1.模式匹配

    模式匹配的模型大概是这样的:给定两个字符串变量S和P,其中S成为目标串,其中包含n个字符,P称为模式串,包含m个字符,其中m<=n。从S的 给定位置(通常是S的第一个位置)开始搜索模式P。如果找到,则返回模式P在目标串中的位置(即:P的第一个字符在S中的下标)。如果在目标串S中没有找 到模式串P,则返回-1.这就是模式匹配的定义啦,下面来看看怎么实现模式匹配算法吧。

2.朴素的模式匹配

    朴素的模式匹配算法非常简单,容易理解,大概思路是这样的:从S的第一个字符S0开始,将P中的字符依次和S中字符比较,若S0=P0 && …… && Sm-1 = Pm-1,则证明匹配成功,剩下的匹配无需进行了,返回下标0。若在某一步Si != Pi 则P中剩下的字符也不用比较了,不可能匹配成功了,然后从S中第二个字符开始与P中第一个字符进行比较,同理,也是知道Sm = Pm-1或者找到某个i使得Si != S-1为止。依次类推若知道以S中第n-m个开始字符为止,还没有匹配成功则证明S中不存模式P。(想想为什么这里强调是n-m)这个代码实现应该是非常 简单的,具体开始参考strstr函数的内部实现。可以看看百度百科,给个链接http://baike.baidu.com/view/745156.htm,这里不写出来了,还得赶紧进入正题KMP呢。

3.快速模式匹配算法(KMP)

    朴素的模式匹配效率不高的主要原因是进行了重复的字符比较。下一次比较和上一次比较没有任何的联系,是朴素模式匹配的缺点,其实上一次比较的比较结果是可 以利用的,这就产生了快速模式匹配。在朴素的模式匹配中,目标串S的下标移动是一步一步的,这其实并不好,移动步数没有必要为1。

  现在不妨假设,当前匹配情况是这样的:S0 …… St St+1 …… St+j  与 P0 P1…… Pj ,现在正在尝试匹配的字符是St+j+1和Pj+1,并且St+j+1 != Pj+1,言外之意就是说S St+1……St+j和P0 P1……Pj是完全匹配的。那么这个时候,S中下一次匹配开始位置应该是什么呢??按照朴素的模式匹配,下次比较应该从St+1开始,并且令St+1和 P0比较,但是在快速模式匹配中并不是这样,快速模式匹配选择St+j+1和Pk比较,K是什么呢?K是这样的一个值,使得P0 P1……Pk 和 Pj-k Pj-k+1……Pj完全匹配,不妨设k=next[j],因此P0 P1……Pk和St+j-k St+j-k+1 ……St+j完全匹配。那么下一次要进行匹配的两个字符应为St+j+1和Pk+1。S和P都没有回溯到下标0在进行比较,这就是KMP之所以快的原因 啦。

    现在关键问题来了,这个K怎么能得到呢?如果得到这个K值复杂度高,那这个思路就不好了,其实这个K呢,只和模式串P有关系,并且要求m个k,k = next[j],因此只要算一次存储到next数组中就可以了,并且时间复杂度和m有关系(线性关系)。看看具体怎么求next数组的值,即求k。

用归纳法求next[]:设next(0) = -1,若已知next(j) = k,欲求得next[j+1]。

(1)如果Pk+1 = Pj+1,显然next[j+1] = k+1.如果Pk+1 != Pj+1,则next[j+1] < next[j],于是寻找h < k 使得P0 P1……Ph = Pj-h Pj-h+1……Pj = Pk-h Pk-h+1……Pk。也就是说h = next(k);看出来了吧,这是个迭代的过程。(也就是以前的结果对求以后的值有用)

(2)如果不存这样的h,说明P0 P1……Pj+1中没有前后相等的子串,因此next[j+1] =-1.

(3)如果存在这样的h,继续检验Ph和Pj是否相等。知道找到这中相等的情况,或者确定为-1求next[j+1]的过程结束。

看看实现的代码:

View Code
 1 int next[20={0};
 2 //注意返回结果是一个数组next,保存m个k值得地方,即若next[j]=k
 3 //则str[0]str[1]…str[k] = str[j-k]str[j-k+1]…str[j]
 4 //这样当des[t+j+1]和pat[j+1]匹配失败时,下一个匹配位置为des[t+j+1]和next[j]+1
 5 void Next(char str[],int len)
 6 {
 7     next[0= -1;
 8     for(int j = 1 ; j < len ; j++)
 9     {
10         int i = next[j-1];
11         while(str[j] != str[i+1&& i >= 0)//迭代的过程
12         {
13             i = next[i];
14         }
15         if(str[j] == str[i+1])
16         {
17             next[j] = i+1;
18         }
19         else
20         {
21             next[j] = -1;
22         }
23     }
24 }

现在有了next数组保存的k值,就可以实现KMP算法了:

View Code
 1 View Code 
 2 
 3 //des是目标串,pat是模式串,len1和len2是串的长度
 4 int kmp(char des[],int len1,char pat[],int len2)
 5 {
 6     Next(str2,len2);
 7     int p=0,s=0;
 8     while(p < len2  && s < len1)
 9     {
10         if(pat[p] == des[s])
11         {
12             p++;s++;
13         }
14         else
15         {
16             if(p==0
17             {
18                 s++;//若第一个字符就匹配失败,则从des的下一个字符开始
19             }
20             else
21             {
22                 p = next[p-1]+1;//用失败函数确定pat应回溯到的字符
23             }
24         }
25     }
26     if(p < len2)//整个过程匹配失败
27     {
28         return -1;
29     }
30     return s-len2;
31 }

时间复杂度:
  对于Next函数近似接近O(m),KMP算法的时间复杂度为O(n),所以整个算法的时间复杂度为O(n+m)

空间复杂度:

  多引入了O(m)的空间复杂度。

4.应用KMP的一道面试题 

  给定两个字符串是s1和s2,要判定s2是否能够被s1做循环移位得到的字符串包含。例如s1=AABCD,s2 =CDAA,返回true,因为s1循环移位可以变成CDAAB。给定s1=ACBD和s2=ACBD则返回false。

      分析:不难发现对s2移位得到的字符串都将是字符串s1s1的子串,如果s2可以有s1循环移位得到,那么s2一定是s1s1的子串,这时KMP算法是不是就很管用了呢。

思考:有没有比KMP更好的思路呢??

posted @ 2012-07-09 21:37 小胡子 阅读(157) | 评论 (0)编辑 收藏

1  OpenEJB概述

      Tomcat本不支持部署EJB,通过向其安装OpenEjb,可使其支持。

2  安装

2.1 下载

http://www.apache.org/dyn/closer.cgi/openejb/3.1.3/openejb.war

2.2  安装

1、将下载的openejb.war 放在Tomcat的安装目录 webapps下。

2、启动Tomcat

3、IE中输入:http://localhost:8080/openejb 回车后显示如下信息:

Welcome to the OpenEJB/Tomcat integration!

 

Now that OpenEJB has been installed, click on the "Testing your setup" link below to verify it. When everything is setup well, feel free to play around with the tools provided below!

 

OK!安装成功,就这么简单!

4、测试一下:http://localhost:8080/openejb/viewjndi.jsp

3  部署

像往常一样,开发一个Ejb工程。

接口:

@Remote

public interface GreeterRemote

{

    public String greet(String message);

    public List<Greeting> getAllGreetings();

}

实现类:

@Stateless

public class GreeterBean implements GreeterRemote,GreeterLocal{

    public List<Greeting> getAllGreetings(){

       

        List<Greeting> greetings = new ArrayList<Greeting>();

        Greeting greeting = new Greeting();

        greeting.setId(12);

        greeting.setName("bill gates");

        greetings.add(greeting);

        greeting = new Greeting();

        greeting.setId(334);

        greeting.setName("李宁");

        greetings.add(greeting);

        return greetings;

    }

 

    public String greet(String message){

       return "您好"+ message;

    }

}

 

      将此EJB工程打成jar包,比如放在D:\Tomcat\ejb下。注:“D:\Tomcat\ejb”是我自己建的。

     然后打开:Tomcat\conf\openejb.xml,</openejb>前的内容改为:

<!--

#

# The <Deployments> element can be used to configure file

# paths where OpenEJB should look for ejb jars or ear files.

#

# See http://openejb.apache.org/deployments.html

#

# The below entry is simply a default and can be changed or deleted

<Deployments dir="apps/" />原来是这句,我们用不到,可以用下面的语句直接加载指定的ejb jar包。

-->

<Deployments jar="D:/Tomcat/ejb/OpenEjbTest.jar" />

重启Tomcat,在浏览器输入:http://127.0.0.1:8089/openejb/invokeobj.jsp

 

 

点击其中的”Browse for an EJB”,转到如下界面:

 

 

如果在其中能看到自己的EJB Bean,那就成功了。

4  客户端调用

按照官方给出的说明:http://openejb.apache.org/3.0/clients.html,此处使用“Remote Client with HTTP (in tomcat)”方式。对上面部署的EJB调用的客户端代码如下:

public class GreeterBeanTest{

   

    public static void main(String[] args) throws NamingException {

      

       Properties p = new Properties();

       p.put("java.naming.factory.initial", "org.apache.openejb.client.RemoteInitialContextFactory");

       p.put("java.naming.provider.url", "http://localhost:8089/openejb/ejb");

      

       InitialContext initialContext = new InitialContext(p);

      

       GreeterRemote greeterRemote =(GreeterRemote) initialContext.lookup("GreeterBeanRemote");

       String str="屈剑峰";

       System.out.println(greeterRemote.greet(str));

    }

}


原文地址:
http://qujianfeng.iteye.com/blog/793409

posted @ 2012-07-09 13:24 小胡子 阅读(3668) | 评论 (1)编辑 收藏

简介

Eclipse 平台允许使用可插入组件 —— 插件 —— 帮助创建丰富的图形用户界面(graphical user interface,GUI)应用程序。例如,插件可以向 GUI 提供视图。但是,在现实的应用程序中,UI 视图不能是孤立的。它们需要根据其他视图的状态进行交互和对本身进行更新。

一个简单的例子是描述世界各地的主要旅游目的地的 GUI 应用程序。这个 GUI 可能有一个 Select City 视图,用于显示旅游景点和公共交通信息。


图 1. 视图链接的例子
Eclipse 视图链接

本文介绍在 Eclipse 中结合视图的方式以及如何对其他视图的状态做出响应。还讨论链接视图方式在哪些情况下可能比其他方式合适。

Eclipse 开发人员可以依赖以下方法对视图进行链接:

  1. 选择提供器 - 选择监听器(selection provider-selection listener)模式,从而让视图对其他视图中的选择做出反应
  2. IAdaptable 接口,与某些事件结合使用
  3. 属性改变监听器,它允许视图将属性改变事件告之已注册的监听器

选择提供器 - 选择监听器范型

选择提供器 - 选择监听器模式能够方便地创建对其他视图中的改变做出响应的视图。例如,当用户点击代表城市名的 UI 项时,另一个视图可能需要显示这个城市的景点详情。这样的视图可以使用 UI 选择对象(可能是代表城市名的字符串对象)中包含的信息,并使用它从模型中获取和显示其他信息。

视图应该能够识别并利用 UI 选择事件。org.eclipse.ui.ISelectionListener 是接收 UI 选择事件的监听器接口。选择监听器必须注册到工作台页面。工作台页面实现 org.eclipse.ui.ISelectionService 接口定义的服务,从而将 UI 选择事件告之监听器。选择监听器必须注册到选择服务。

用于显示可选择的 UI 项的视图还应该能够公布 UI 选择。视图通过将 “选择提供器” 注册到它们各自的工作台站点来实现这一点。Eclipse 中的每个 UI 部分通过 org.eclipse.ui.IWorkbenchPartSite 引用与工作台站点联络。选择提供器注册到工作台站点。

在使用选择提供器 - 选择监听器模式链接视图时,视图可以将本身作为监听器添加到工作台页面,而希望公布选择的其他视图必须将选择提供器添加到它们各自的工作台站点。org.eclipse.ui.ISelectionListener 接口如下所示。

public void selectionChanged(IWorkbenchPart part, ISelection selection);

要使视图能够监听选择改变,视图必须实现 ISelectionListener 接口并必须将自己注册到工作台页面。清单 1 显示一个例子。


清单 1. 将选择监听器添加到工作台页面
 1 public class MyView extends ViewPart implements ISelectionListener {
 2     public void createPartControl(Composite parent) {
 3         // add this view as a selection listener to the workbench page
 4         getSite().getPage().addSelectionListener((ISelectionListener) this);
 5     }
 6 
 7     // Implement the method defined in ISelectionListener, to consume UI
 8     // selections
 9     public void selectionChanged(IWorkbenchPart part, ISelection selection) {
10         // Examine selection and act on it!
11     }
12 }
				 

使用 UI 选择的更好的方法是,将消费者视图作为监听器注册到特定的视图部分。正如在下面的例子中可以看到的,源视图部分的视图 ID 在注册选择监听器期间被作为一个参数。


getSite().getPage().addSelectionListener("SampleViewId",(ISelectionListener)this);


这种方式可以避免对消费者视图进行多余的回调,如果视图被注册为非特定的监听器,就会出现这种情况。

清单 2 中的代码片段显示一个视图的 createPartControl() 方法,这个方法创建一个 JFace TableViewer 并将它作为选择提供器添加到工作台站点。这些代码使 TableViewer 中的任何 UI 选择改变能够传播到页面,并最终传播到对这种事件感兴趣的消费者视图。


清单 2. 设置选择提供器
 1     public void createPartControl(Composite parent) {
 2         // Set up a JFace Viewer
 3         viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
 4         viewer.setContentProvider(new ViewContentProvider());
 5         viewer.setLabelProvider(new ViewLabelProvider());
 6         viewer.setSorter(new NameSorter());
 7         viewer.setInput(getViewSite());
 8         // ADD the JFace Viewer as a Selection Provider to the View site.
 9         getSite().setSelectionProvider(viewer);
10     }

这个视图将它创建的 JFace TableViewer 注册为选择提供器有两个原因:

  1. 这个视图打算使用这个 TableViewer 显示信息,而且用户将与 TableViewer 进行交互。
  2. TableViewer 实现了选择提供器接口并能够向工作台部分站点传播选择事件。

因为 JFace 查看器是选择提供器,所以在大多数情况下就不必创建选择提供器了。视图只需使用众多的 JFace 查看器之一来显示信息,并将 JFace 查看器注册为选择提供器。

另一种链接方式

某些情况需要另一种视图链接方式:

  1. 信息量可能太大,由于内存使用量增加,UI 选择对象无法有效地容纳它。
  2. 视图可能希望公布其他信息,而不只是公布可视化选择信息。公布的信息可能是根据选择进行某些后期处理的结果。
  3. 视图可能希望使用来自另一个插件的信息,而这个插件可能根本没有提供视图(使用包含的 JFace 查看器)。在这种情况下,使用基于 UI 选择的链接是不可能的。

可以使用 org.eclipse.core.runtime.IAdaptable 接口来缓解第一个问题,这个接口使选择对象能够在需要时传播更多信息。第二个和第三个问题需要用手工方式解决,属性改变监听器模式是合适的解决方案。

使用 IAdaptable 接口

实现 IAdaptable 接口的类能够动态地返回某些类型的适配器,然后可以使用这些适配器获取更多信息。如果查看器中的选择对象实现了 IAdaptable 接口,那么根据它们可以返回的适配器类型,可以有效地获取更多信息或相关信息。org.eclipse.core.runtime.IAdaptable 接口如下所示。


public void object getAdapter(Class adapter);


显然,调用者应该知道它期望选择返回的适配器接口类型。考虑一个 JFace TreeViewer,它在一个单层的树中显示城市。代表城市的对象是 CityClass 类型的。CityClass 对象应该包含关于此城市的基本信息,并只在需要时返回详细信息。在清单 3 中要注意,CityClass 支持的适配器类型使调用者能够在需要时获得更多信息。


清单 3. JFace TreeViewer 中的 CityClass
 1 class CityClass implements IAdaptable {
 2     private String cityName;
 3 
 4     public CityClass(String name) {
 5         this.name = name;
 6     }
 7 
 8     public String getName() {
 9         return name;
10     }
11 
12     public CityClass getParent() {
13         return parent;
14     }
15 
16     public String toString() {
17         return getName();
18     }
19 
20     public Object getAdapter(Class key) {        
21         if (key.getName().equals("ITransportationInfo"))         
22             return CityPlugin.getInstance().getTransportAdapter();        
23         else (key.getName().equals("IPlacesInfo"))        
24         return CityPlugin.getInstance().getPlacesAdapter();       
25         return null;       }
26 }
27 

熟悉 Eclipse IDE 工作台的开发人员都了解 Outline 视图,这个视图提供了编辑器中打开的文件的结构化视图。这个 Outline 视图展示了 IAdaptable 接口如何与某些事件类型结合使用,从而有效地根据其他视图的内容对视图进行初始化。编辑器必须为用户打开的文件创建一个 Content Outline 页面。Content Outline 页面符合 IContentOutlinePage 接口。编辑器还必须实现 IAdaptable 接口,这样就能够向编辑器查询 IContentOutlinePage 类型的适配器。使用这个适配器来获取和显示文件的大纲信息。

IAdaptable 接口的另一个例子是 Properties 视图。Properties 视图跟踪对活动部分的选择,并调用当前选择对象上的 getAdapter 方法。查询的适配器类型是 IPropertySource。然后,Properties 视图使用 IPropertySource 适配器来获取要显示的信息。

在这些视图链接例子中,应用程序在接到 Selection Changed 或 Part Activation 通知时,通过 IAdaptable 获取信息。因此,如果选择对象实现了 IAdaptable,那么与从选择对象本身获取的信息量相比,用户可以通过适配器获得多得多的信息。


属性改变监听器范型

可以使用属性改变监听器类型的交互来解决前面提到的另外两个问题:视图如何使用来自未提供视图的插件的信息,以及视图如何公布在可视化选择之后某些处理所生成的信息?

可以建立一个插件来接受属性改变监听器的注册,并在需要时通知注册的监听器。应用程序可以将定制的事件告之监听器,事件中还可以包含要共享的信息。

与选择提供器的情况不同,属性改变提供器不需要实现特定的接口。您必须决定将监听器注册到提供器的语义。清单 4 中的代码片段是一些方法,它们允许在属性提供器视图或插件类中添加或删除属性改变监听器。


清单 4. 添加和删除属性改变监听器
1 //To add a listener for property changes to this notifier:   
2 public void addPropertyChangeListener(IPropertyChangeListener listener); 
3 //To remove the given content change listener from this notifier:   
4 public void removePropertyChangeListener(IPropertyChangeListener listener);

属性提供器应该使用 org.eclipse.jface.util.PropertyChangeEvent 来创建一个可以有效填充和传播的事件。另外,属性提供器要负责维护监听器列表并对它们进行回调。

作为一个例子,请考虑一个每小时调用 World Weather Web Service 来查询主要城市的气象的插件,它要使这些信息可供其他插件和视图使用。CityWeatherPlugin 可以公开一个称为 CitiesWeatherXML 的属性,消费者可以将本身作为 PropertyChange 监听器注册到 CityWeatherPlugin。为此,监听器必须了解 CityWeatherPlugin 中的注册方法,这样才能将本身注册为气象数据事件的监听器。CityWeatherPlugin 应该跟踪并通知监听器。它使用 PropertyChangeEvent 向监听器提供数据。


清单 5. 创建属性提供器
 1 class CityPopulationPlugin {
 2     ArrayList myListeners; // A public method that allows listener registration
 3 
 4     public void addPropertyChangeListener(IPropertyChangeListener listener) {
 5         if (!myListeners.contains(listener))
 6             myListeners.add(listener);
 7     }
 8 
 9     // A public method that allows listener registration
10     public void removePropertyChangeListener(IPropertyChangeListener listener) {
11         myListeners.remove(listener);
12     }
13 
14     public CityPopulationPlugin() {
15         // method to start the thread that invokes the population \ web service
16         // once every hour
17         // and then notifies the listeners via the propertyChange() callback
18         // method.
19         initWebServiceInvokerThread(myListeners);
20     }
21 
22     void initWebServiceInvokerThread(ArrayList listeners) {
23         // Code to Invoke Web Service Periodically, and retrieve information
24         // Post Invocation, inform listeners
25         for (Iterator iter = listeners.iterator(); iter.hasNext();) {
26             IPropertyChangeListener element = (IPropertyChangeListener) iter.next();
27             element.propertyChange(new PropertyChangeEvent(this"CitiesWeatherXML"null,
28                     CityWeatherXMLObj));
29         }
30     }
31 }

属性改变监听器必须实现 org.eclipse.jface.util.IPropertyChangeListener 接口,以便允许属性改变提供器对它进行回调。这个接口有一个方法


public void propertyChange(PropertyChangeEvent event)

清单 6. 实现 IPropertyChangeListener

 1 class MyView implements IPropertyChangeListener {
 2     public void createPartControl() {
 3         // register with a Known Plugin that sources Population Data
 4         CityPopulationPlugin.getInstance().addPropertyChangeListener(this);
 5     }
 6 
 7     public void propertyChange(PropertyChangeEvent event) {
 8         // This view is interested in the Population Counts of the Cities.
 9         // The population data is being sourced by another plugin in the
10         // background.
11         if (event.getProperty().equals("CitiesWeatherXML")) {
12             Object val = event.getNewValue();
13             // do something with val
14         }
15     }
16 }

这种方式的灵活性在于,应用程序可以在需要时通知监听器,并根据各种场景向它们传递信息。传递的信息不必直接与 UI 选择相关;这些信息可以是某些后期处理的结果。另外,它可以与其他后台作业的状态相关,或者是定期从模型中获取的最新信息。例如,City Selector View 可能不只是传播选择的城市,还使用 PropertyChange 范型将当前选择的城市的气象信息异步地传播给其他消费者。


结束语

本文讨论了使视图相互协作和响应的各种方式。如果 UI 选择本身不够,可以使用 IAdaptable 接口加强它们。属性改变监听器也为满足非 UI 场景提供了更大的灵活性。


http://www.ibm.com/developerworks/cn/opensource/os-ecllink/
http://www.cnblogs.com/zephyr/archive/2008/05/30/1210477.html
http://www.eclipse.org/articles/Article-Integrating-EMF-GMF-Editors/

posted @ 2012-07-05 16:14 小胡子 阅读(1308) | 评论 (2)编辑 收藏

http://sishuok.com/forum/blogCategory/showByCategory.html?categories_id=51&user_id=4249
http://sishuok.com/forum/blogCategory/showByCategory.html?categories_id=49&user_id=2

posted @ 2012-07-02 13:46 小胡子 阅读(149) | 评论 (0)编辑 收藏

http://sishuok.com/forum/blogCategory/showByCategory/7.html?user_id=183

posted @ 2012-07-02 13:41 小胡子 阅读(131) | 评论 (0)编辑 收藏

http://sishuok.com/forum/blogCategory/showByCategory.html?categories_id=82&user_id=6091

posted @ 2012-07-02 13:29 小胡子 阅读(169) | 评论 (0)编辑 收藏

maven是什么
maven这个词可以翻译为“知识的积累”,也可以翻译为“专家”或“内行”。作为apache组织中的一个颇为成功的开源项目,maven主要服务于基于java平台的项目构建、依赖管理和项目信息管理。
maven能干什么
使项目构建构成更容易;
提供统一构建系统(编译、测试、持续整合...);
提供高质量的项目信息(依赖、报告、site...);
提供开发的最佳实践指南;
能无缝的加入新的特性;
maven有什么【maven的核心概念】
项目对象模型(Project Object Model),
坐标(Coordinates),
项目生命周期(ProjectLifecycle),
插件(plugin)和目标(goal),
依赖管理系统(Dependency Management System),
仓库管理(Repositories)。
准备我们需要的环境
下载maven的安装包apache-maven-3.0.3-bin.tar.gz,解压至任何目录。
设置环境变量M2_HOME,设置为maven的安装路径;同时把maven的bin目录增加至环境变量path里。【我们可以看到,跟java的安装几乎一模一样】。
正常情况下,maven会到中央仓库去下载我们需要的构件或者插件;但是,现在在教室不能上网,所以需要将老师的私服加入到下载的配置中。建立 C:\Documents and Settings\Administrator\.m2文件夹,其中Administrator为当前登录的用户。拷入准备好的 settings.xml。其中

java代码:
  1. <profile>   
  2. 。  
  3.       <url>http://10.83.1.111:10080/nexus-webapp-1.9.1.1/content/groups/public/</url>   
  4.       <url>http://10.83.1.111::10080/nexus-webapp-1.9.1.1/content/groups/public/</url>   
  5. </profile>  
代表老师的私服
工程描述文件pom

java代码:
  1. <project xmlns="http://maven.apache.org/POM/4.0.0"  
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">  
  4. <modelVersion>4.0.0</modelVersion>  
  5. <groupId>cn.javass.study</groupId>  
  6. <artifactId>hello-world</artifactId>  
  7. <version>1.0-SNAPSHOT</version>  
  8. <name>Maven Quick Start Archetype</name>  
  9. <dependencies>  
  10. <dependency>  
  11. <groupId>junit</groupId>  
  12. <artifactId>junit</artifactId>  
  13. <version>3.7.1</version>  
  14. <scope>test</scope>  
  15. </dependency>  
  16. </dependencies>  
  17. </project>  
常见的mvn命令

java代码:
  1. mvn clean test  
  2. mvn clean package  
  3. mvn clean install  
mvn是命令名
clean说明要清空所有的配置文件
test说明要运行单元测试
package说明要打包
install安装到本地仓库
坐标
Maven的世界中拥有数量非常巨大的构件,也就是我们平时用的一些jar、war等文件。Maven定义了这样一组规则:世界上任何一个构件都可以使用 Maven坐标唯一标识。Maven坐标的元素包括groupId、artifactId、version、packaging、classifier。
groupId:定义当前Maven项目隶属的实际项目。groupId的表示方式与java包名的表示方式类似,通常与域名反向一一对应。
artifactId:该元素定义实际项目中的一个Maven项目/模块。
version:版本【可以分成稳定版本和快照版本】。
packaging:打包方式。如:jar、war。
classifier:不能直接定义,用来表示构件到底用于何种jdk版本。
pom
POM(Project Object Model):Maven的核心文件,位于每个工程的根目录中,指示Maven如何工作的元数据文件,类似于Ant中的build.xml文件。
 
依赖(Dependency)
为了能够构建或运行,Java工程一般会依赖其它的包。在Maven中,这些被依赖的包就被称为dependency。dependency一般是其它工程的坐标。
依赖具有传递性。
生命周期
项目的生命周期是指软件开发人员每天都在对项目进行清理、编译以及部署。虽然大家都在不停的做构建工作,但公司和公司间、项目和项目间,往往使用不同的方式做类似的工作。
Maven的生命周期就是为了所有的构建过程进行抽象和统一。这个生命周期包含了项目的清理、初始化、编译、测试、打包、集成测试、验证、部署和站点生成等几乎所有构建步骤。
Maven拥有三套相互独立的生命周期,他们分别为clean、default和site。clean生命周期的目的是清理项目,default生命周期的目的是构建项目,而site生命周期的目的是建立项目站点。
阶段【phase】
每个生命周期包含一些阶段,这些阶段是有顺序的,并且后面的阶段依赖于前面的阶段,用户和Maven最直接的交互方式就是调用这些生命周期阶段。
较之于生命周期阶段的前后依赖关系,三套生命周期本身是相互独立的,用户可以仅仅调用clean生命周期的某个阶段,或者仅仅调用default生命周期的某个阶段,而不会对其他生命周期产生任何影响。
clean生命周期包含三个阶段:
pre-clean、clean、post-clean
default生命周期包含很多阶段:
site生命周期包含四个阶段:
pre-site、site、post-site、site-deploy
插件及其目标【goal】
Maven的核心仅仅定义了抽象的生命周期,具体的任务是交由插件完成的,插件以独立的构建形式存在。
对于插件本身,为了能够复用代码,它往往能够完成多个任务。例如maven-dependency-plugin插件,能够基于项目以来做很多事情。比 如,能够分析项目依赖,找到无用的或者重复的依赖;还能够列出项目的依赖树。这些功能往往背后有很多可以复用的代码,因此,可以把这些功能聚集在一个插件 里,每个功能就是一个插件目标。
我们原来仅仅学过通过前缀调用插件,现在可以用冒号来指定调用插件的某个具体目标了,比如:mvn dependency:tree。冒号前面是插件的前缀,冒号后是该插件的目标。
在Maven世界中,任何一个依赖、插件或者项目的构建输出,都可以称为构件。任何一个构件都有一组坐标唯一标识。
得益于坐标机制,任何Maven项目使用任何一个构件的方式都是完全相同的。在此基础上,Maven可以在某个位置统一存储所有Maven项目共享的构件,这个统一的位置就是仓库。
对于Maven来说,仓库只分为两大类:本地仓库和远程仓库。当Maven根据坐标寻找构件的时候,它首先会查看本地仓库,如果本地仓库存在此构件,则直 接使用;如果本地仓库部存在此构件,Maven就会去远程仓库查找,发现需要的构件之后,下载到本地仓库再使用。
【以上概念,对比一下hibernate中的一级缓存。】
私服是一种特殊的远程仓库,它是架设在局域网内的仓库服务,私服代理广域网上的远程仓库,供局域网内的Maven用户使用。
 
首先从 http://nexus.sonatype.org/downloads/下载最新版本的Nexus的war包形式版本。
登录时其默认的用户名为admin,密码为admin123。
使用nexus的时候,如果想支持搜索;可以从远程仓库下载其索引,这当然非常慢,我们可以使用gui的方式在下载好的构件基础上重建索引。
exus支持非常全面的搜索方式。GAV搜索:通过GroupId、ArtifactId和Version进行搜索;全类名搜索;关键词搜索。
配置客户机使用nexus时,需要在settings.xml文件中加入相应的配置,从nexus的仓库组中下载构件和插件。【在helloworld中已经说明,这里不再举例】
WTP项目【也就是咱们的eclipse下的dynamic web project】
src/main/java:源码目录
src/main/resources:资源目录(如存放log4j.properties)
src/main/webapp:web目录【它下面就是WEB-INF】
src/test/java:测试源码目录
src/test/resources:测试资源目录
target:编译结果目录
安装插件
将获得的m2eclipse.rar解压到eclipse的dropins文件夹下,重启eclipse,就可以看到maven插件了。
新建项目
选择【New】->【Project】->【Maven Project】,选择create a simple project,填入GAV,选择打包方式jar/war。
如果要新建web工程,请选择打包方式为war,为了让项目能在eclipse相关的tomcat下运行,还需要一点点麻烦的配置。选择项目的属性,添加 dynamic web project支持【project facets】;再次选择项目的属性,重整项目的部署【deployment assembly】。这部分操作非常非常麻烦,请注意看老师的演示。
添加依赖
在项目上右击【maven】->【add dependency】。
运行命令行
在项目上右击【Run as】。
 
视频配套PPT,视频地址【 独家maven基础实战视频课程】 

posted @ 2012-07-02 13:28 小胡子 阅读(208) | 评论 (0)编辑 收藏

 encodeURI(path) : 对文件路径进行编码,解决中文问题

posted @ 2012-06-17 16:21 小胡子 阅读(115) | 评论 (0)编辑 收藏

通过查看jquery API,发现jquery还有一个 complete对象,是请求完成后回调函数 (请求成功或失败之后均调用)。 同时有两个参数XMLHttpRequest, textStatus。所以,我们只需要在请求完成后,将传回的XMLHttprequest对象手工回收即可,代码如下:

$.ajax({
    url: "http://www.aizr.net",
    data: { name: "xxxx" },
    dataType: "xml",
    success: function (data, textStatus) { 
       //do something...
    },
    complete: function (XHR, TS) { XHR = null }
})

posted @ 2012-06-12 12:47 小胡子 阅读(300) | 评论 (0)编辑 收藏

仅列出标题
共15页: First 上一页 7 8 9 10 11 12 13 14 15 下一页