学习笔记之JAVA图形设计卷I AWT——第3章 图 形
前时显示器坏了,file://写前言:我觉得写的不是学习笔记,倒象教程。我是想让有所获,故详细了点。
注意1:在AWT中提供的用户接口构件(如按钮、列表、菜单、对话框等)不包含一些类似的纯粹的绘制图形的对象(如Line或Circle类)
详细意思:由于原始的AWT在设计时不允许纯粹的绘制图形的对象,那么Rectangle、Polygon和Point没有任何绘制图形的能力。换句话说,
Rectangle、Polygon和Point不具备draw方法。您可做的,仅仅是可以设置和得到它们代表的几何实体的信息。
为了代替那些纯粹的、可绘制图形的对象,AWT使用了一种简单的——尽管不够灵活并且不易扩展——模式:
每个AWT构件完全来自于它自己的java.awt.Graphics对象,尽管其中的一些图形操作可以在与之相关的构件中实现。
Graphics也可以向种各样的输出设备中绘制,像画面外缓冲器和打印机——参见第24章“双缓冲技术”和18.4节“打印”中的相关内容。
请先看两张表(都忽略了java.awt.peer方法):
表3-1 传递一个对Graphics的引用的JDK方法
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
软件包类方法
─────────────────────────────────
java.awtCanvas paint(Graphics g)
Componentpaint(Graphics g)
ComponentpaintAll(Graphics g)
Componentprint(Graphics g)
ComponentprintAll(Graphics g)
Componentupdate(Graphics g)
Containerpaint(Graphics g)
ContainerpaintComponents(Graphics g)
Containerprint(Graphics g)
ContainerprintComponents(Graphics g)
ScrollPaneprintComponents(Graphics g)
java.beansProperty_EnditorpaintValue(Graphics g,Rectangle r)
Property_EnditorSupport paintValue(Graphics g,Rectangle r)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
表3-2返回Graphics 引用的JDK方法
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
软件包类方法
─────────────────────────────────
java.awtComponentgetGraphics()---(为最常使用)
ImagegetGraphics()
PaintJobgetGraphics()
Graphics create()
Graphics create(int x,int y,int w,int h)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1.java.awt.Graphics 作用:定义一个真正的工具,用来接受图形操作(显示图像和文本、绘制和填充开关、剪贴图像操作等)
几乎在所有的applet程序(和应用程序)中,都使用了AWT处理Graphics来为图像服务。
某applet程序片断:
public void paint(Graphics g){
g.drawString("Hello Graphics Java World",75,100);
...}
另外,在构件内部执行图像操作时,每个Graphics中都保持了下面的图形属性:
·用来绘制和填充形状的颜色。
·用来描述文本的字体。
·剪贴矩形。
·绘制模式(XOR或Paint)。
·用于显示和剪贴坐标的平移原点。
2.Graphics参数
Graphics类的两个主要的职责:
(1)设置和获取图形参数
(2)在输出设备中执行图形操作(为主)
Graphics类中使用的四个参数
表3-3
| 方法
-----------------------|----------------------------------------------------------------------------
(a)颜色 void setColoer(Color color)
Color getColor()
-----------------------|----------------------------------------------------------------------------
(b)字体 void setFont(Font f)
和 Font getFont()
-------------------------------------------------------------------
字体度量(属性为只读) FontMetrics getFontMetrics() 返回的字体度量,和Graphics当前的字体结合在一起
FontMetrics getFontMetrics(Font f) 返回的字体尺度和指定的字体结合在一起
-----------------------|----------------------------------------------------------------------------
(c)剪贴矩形 void setClip(int x,int y,int w,int h)
Rectangle getClipBounds()
void setClip(Shape) Shape接口是java 2D API的一部分,Shape所表示的形状可以是非矩形的,
所以它可以为输出设备定义一个非矩形的剪贴矩形
Shape getClip()
void clipRect(int x,int y,int w,int h) 计算一个新的剪贴矩形,该剪贴矩形是原先剪贴矩形和
方法中参数指定的剪贴矩形的交集
-----------------------|----------------------------------------------------------------------------
(d)图形模式(属性为只写) void setPaintMode()(默认模式) 设置paint图形模式,意味着后面的着色操作将改写现有的图形
void set setXORMode() 允许绘制和擦掉现在图形而不干扰其下面的图形
-----------------------|-------------------------------------------------------------------------------------------
简单的applet例子 却要注意:
例1.
import java.applet.Applet;
import java.awt.*;
public class RectTest extends Applet{
public void paint(Graphics g){
g.drawRect(2,2,4,4);//*****行A
}
}
file://随便一个HTML文件如001.html,
<html>
<title>Sample Applet</title>
<body>
<applet code="RectTest.class" width=200 height=200></applet>
</body>
</html>
file://命令行下输入:appletviewer 001.html,
所绘制的坐标路径如下所示:为5像素单位见方的矩形
(2,2)→(6,2)→(6,6)→(2,6)→(2,2) 画笔所采用的颜色则是通过调用Graphics.setColor(Color)来指定的。
注意:
通过调用Graphics.drawRect()绘制矩形时,其结果将会在矩形的右边和下边各存在一个额外的像素行。这是因为传递到
Graphics.drawRect()中的参数定义的是画笔遵循的路径,而不是矩形自身的尺寸。由于画笔是沿上面所讲坐标路径绘制
矩形的,所以g.drawRect(2,2,4,4)实际上绘制出来的矩形的宽度和高度是5个像素单位——而不是你所想象的4个像素单位
技巧1:《图形坐标位于像素之间》,而不是在它们之上。指定坐标的图形方法指定画笔路径——通常是单位像素见方——的移动。
画笔绘制像素的路径是起点→右→下→左→起点。结果,绘制图形状外形的图形方法将在矩形的右边和下边各存在一个额外的像素行
技巧1的使用:绘制构件的四周边界时
Public void paint(Graphics g){
Dimension size=getSize();
g.drawRect(0,0,size.width-1,size.height-1);//不减一的话,右边缘和底边缘边界将被绘制在构件的外面而看不到该部分。
}
把例1中的//*****行A--->g.drawRect(2,2,4,4);换成g.fillRect(2,2,4,4);
注意:此时传递给fillRect()的参数指定的坐标路径与在前面调用drawRect()时指定的坐标路径相同。但是,填充外形的Graphics
方法将填充路径的内部,所以填充的矩形是4个像素宽和4个像素高
3.Graphics引用
有两种方法要用到对构件的Graphics的引用。这两种方法就是:覆盖前面表3-1中的方法(传递一个对Graphics的引用),或调用表3-2
列出的方法(将返回对Graphics的引用)。值得注意的是从Component、Image和PrintJob中的getGraphics()方法返回的Graphics引
用并不是一个对Graphics引用的引用,而是返回一个初始Graphics的副本。在下面的章节中,你将会发现这是一个很重要的概念。
又一个简单的applet例子:再强调一遍 Graphics引用引用和构件相关的真实Graphics的副本
import java.applet.Applet;
import java.awt.*;
public class CopyTest extends Applet {
public void paint(Graphics g) {
setForeground(Color.yellow);//设置其前景颜色为黄色
g.drawLine(0,0,getSize().width-1, getSize().height-1);
}
}
结果可能不像你所期待那样:在开始画线时,线的颜色可能不是黄的(黑色一闪,用IE看吧。)
原因:调用Component.setForeground(),改变构件中Graphics的当前颜色——在本例中,
该颜色被改变成黄色。SetFroeground()影响applet的Graphics,但并不影响传递给paint()
的Graphics的副本。因此,当第一次调用paint()时,线条的颜色并没有变成黄色。当调用
drawLine()时,传递给paint()的Graphics和实际的Graphics并不同步。后来调用paint()时,
传递给paint()的是applet的Graphics的一个新副本,因此setForeground()的调用结果可以
将applet的Graphics的当前颜色改变成黄色。
如果将该applet程序改一下:
import java.applet.Applet;
import java.awt.*;
public class CopyTest2 extends Applet {
public void paint(Graphics g) {
setForeground(Color.yellow);// the next line would do just as well as the following
// g.setColor(Color.yellow);
Graphics copy = getGraphics();
try {
System.out.println("g=" + g.getColor() +" copy=" + copy.getColor());
copy.drawLine(0,0,getSize().width-1, getSize().height-1);
}
finally {
copy.dispose();
}
}
} file://那么一开始画出的线就是黄色的,DOS下打印出:
g=java.awt.Color[r=0,g=0,b=0] copy=java.awt.Color[r=255,g=255,b=0]
g=java.awt.Color[r=255,g=255,b=0] copy=java.awt.Color[r=255,g=255,b=0]
g=java.awt.Color[r=255,g=255,b=0] copy=java.awt.Color[r=255,g=255,b=0]
解释:传递给paint()的Graphics将被忽略掉,通过调用getGraphics()方法得到一个新的Graphics,
应用这个新的Graphics绘制直线。因为在调用setForeground()之后获取Graphics,所以Graphics
当前的颜色,也就是线条的颜色,将变成黄色。
4.Graphics引用的寿命
注意:除了引用真实的副本外,传递给paint()和update()等方法的Graphics引用仅仅在方法的执行过程中才有效。
一旦方法返回,引用将不再有效(强行重新载入如刷新等可重绘该applet)
例子:
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
public class HoldRef extends Applet {
private Graphics oldg;
private boolean first = true;
public void paint(Graphics g) {
if(first) {
oldg = g;
first = false;
}
oldg.drawLine(0,0,getSize().width-1, getSize().height-1);
}
}
可以看出:传递给方法的Graphics引用寿命很短,因为它们是必须处理的有限资源,每个Graphics表示的图像环境
是由本地的窗口系统提供的。图像环境通常被设定在一个有限的数量内可用,而且当调用返回时传递Graphics引用的
调用者会很仔细地处理掉它。例如,当调用Component.Paint()返回时,调用者处理掉传递给paint()的Graphics。
5.虽然Java带有垃圾收集,但是在AWT中仍有两个地方供开发者处理有限的系统资源,处理Graphics就是其中之一
(注:窗口和对话框也必须被处理,请参见第16章“窗口、框架和对话框”)。在处理Graphics有两个要注意的事项,
即:什么时候需要处理和怎样处理
处理Graphics规则:如果调用表3-2列出的getGraphics方法中的一个得到一个对Graphics的引用,
或者通过Graphics.create()创建一个Graphics,那么就有责任对它们进行处理
通过调用Graphics.dispose()处理Graphics,请看某程序片断:
public void someMethodInAComponent(){//code fragment
Graphics g=getGraphics();
if(g!=null){
try{
file://do something with g- if an exception is thrown,
file://the finally block will be executed
}
finally{
g.dispose()//crucial至关重要,因为忽略不做将会导致窗口系统用完图形环境,这在大部分操作上会引起问题
}
}
}
file://在这里要注意的是对g不做null检测并不是无根据的,如果在构件的同位体创建之前调用getGraphics()则它确实返回null
对Graphics.dispose()的调用被安置在finally块中,而对Graphics的控制则在相应的try块中执行。这保证调用dispose()
将被放在事件内,例外的情况则会被从try块中抛出。
技巧2:《传递图形引用的方法引用副本》
技巧2的使用:
Graphics表示本地图形环境,是一个典型的有限资源。所以,被返回的Graphics引用必须通过调用Graphics.dispose()方法处理。
传递Graphics引用的方法,像Component.paint(),是一种不需要手工设置处理的方法——当调用返回时,方法的调用者必须处理
大家累了吧?我也是。努努力!加加油!
6.绘制和填充形状(直线(Lines)折线(Polylines)矩形(Rectangles)弧(Arcs)椭圆(Ovals)多边形(Polygons)文本(Text)图像(Images))
6.1画直线 使用方法:Graphics.drawLine(int x,int y,int x2,int y2)
AWT不能画不固定宽度的直线,使用图形笔所画的直线,其宽度一般是一个像素。
线经常被绘制成实线——没有规定线的模式,如点线或虚线。但是,在Java 2D API中,为不同的线类型和图形笔尺寸提供广泛的支持。
随机直线例子:起点,长度、方向和颜色都随机
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
public class PickupSticks extends Applet {
private static Color[] colors = {
Color.white, Color.black, Color.blue, Color.red,
Color.yellow, Color.orange, Color.cyan, Color.pink,
Color.magenta, Color.green };
public void init() {
Button button = new Button("scatter");
add(button);
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
repaint();
}
});
}
public void paint(Graphics g) {
for(int i=0; i < 500; ++i) {
int x = (int)(Math.random()*100);
int y = (int)(Math.random()*100);
int deltax = (int)(Math.random()*100);
int deltay = (int)(Math.random()*100);
g.setColor(colors[(int)(Math.random()*10)]);
g.drawLine(x,y,x + deltax, y + deltay);
}
}
}
6.2画折线 使用方法:drawPolyline(int[] xPoints,int[] yPoints,int numPoints)
一个数组指定每个点的x坐标值,另一个数组指定点的y坐标值,要画的折线的点数
点数等于线段数+1,如果起始点和终点不重合,所画的折线是不封闭的
折线例子:
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
public class Polylines extends Applet {
private static Color[] colors = {
Color.white, Color.black, Color.blue, Color.red,
Color.yellow, Color.orange, Color.cyan, Color.pink,
Color.magenta, Color.green };
public void init() {
Button button = new Button("repaint");
add(button);
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
Polylines.this.repaint();
}
});
}
public void paint(Graphics g) {
file://int arraySize = ((int)(Math.random()*100));//原例子
int arraySize =4;//线数过多,故我设为4
int[] xPoints = new int[arraySize];
int[] yPoints = new int[arraySize];//xPoints和yPoints要相等
file://int[] yPoints = new int[arraySize-1];//不相等,编译时无错,运行时给你个惊喜:)。
for(int i=0; i < xPoints.length; ++i) {
xPoints[i] = ((int)(Math.random()*200));
yPoints[i] = ((int)(Math.random()*200));
}
g.setColor(colors[(int)(Math.random()*10)]);
g.drawPolyline(xPoints, yPoints, arraySize);
showStatus(arraySize + " points");
}
}
6.3绘制矩形:
矩形有3种:
(1)实体的(solid)
(2)圆角的(rounded)
(3)3D
绘制方法如下:
void clearRect(int x,int y,int w,int h)
void drawRect(int x,int y,int w,int h)
void drawRoundRect(int x,int y,int w,int h,int arcWidth,int arcHeight)
增加的参数arcWidth用来设置弧的水平直径和arcHeight用来设置竖直方向上的直径
void draw3DRect(int x,int y,int w,int h,boolean raise)
增加的参数raise用来指定3D效果是凸的还是凹的,ture时,3D效果是凸的,false时3D效果是凹的
void fillRoundRect(int x,int y,int w,int h,int arcWidth,int arcHeight)
void fillRect(int x,int y,int w,int h)
void fill3DRect(int x,int y,int w,int h,boolean raise)
绘制矩形的例2:
file://applet程序中配备有三个按钮和一个复选框,绘制矩形的代码被封装在applet的paint方法中
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
public class RandomRectangles extends Applet {
private static Color[] colors = {
Color.white, Color.black, Color.blue, Color.red,
Color.yellow, Color.orange, Color.cyan, Color.pink,
Color.magenta, Color.green };
private int numRects = 10;
private boolean fill = false,//复选框用于指定的矩形是否被填充
raise = false,
round = false,
threeD = false;//变量不能以数字开头如3D样
public void init() {//每个按钮代表被绘制的矩形的类型
Button rectsButton = new Button("矩形");
Button roundButton = new Button("圆角矩形");
Button threeDButton = new Button("3D矩形");
Checkbox fillCheckbox = new Checkbox("填充");
add(rectsButton);
add(roundButton);
add(threeDButton);
add(fillCheckbox);
rectsButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
round = false;
threeD = false;
repaint();
}
});
roundButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
round = true;
threeD = false;
repaint();
}
});
threeDButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
threeD = true;
round = false;
repaint();
}
});
fillCheckbox.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent event) {
fill = ((Checkbox)(event.getSource())).getState();
}
});
}
file://绘制所有的矩形时,采用的都是1像素宽的图形笔,因为在AWT中,只允许这个尺寸
public void paint(Graphics g) {
for(int i=0; i < numRects; i++) {
Point lhc = randomPoint(); // left hand corner
Dimension size = randomDimension();
g.setColor(colors[(int)(Math.random()*10)]);
if(round) {
if(fill)
g.fillRoundRect(lhc.x,lhc.y,size.width,size.height,
(int)(Math.random()*250),
(int)(Math.random()*250));
else
g.drawRoundRect(lhc.x,lhc.y,size.width,size.height,
(int)(Math.random()*250),
(int)(Math.random()*250));
}
else if(threeD) {
file://绘制3D矩形前,Graphics的颜色被设置成亮灰色,才可以看见其3D效果
g.setColor(Color.lightGray);
if(fill)
g.fill3DRect(
lhc.x,lhc.y,size.width,size.height,raise);
else
g.draw3DRect(
lhc.x,lhc.y,size.width,size.height,raise);
}
else {
if(fill)
g.fillRect(lhc.x,lhc.y,size.width,size.height);
else
g.drawRect(lhc.x,lhc.y,size.width,size.height);
}
raise = raise ? false : true;
}
}
private Dimension randomDimension() {
return new Dimension((int)(Math.random()*250),
(int)(Math.random()*250));
}
private Point randomPoint() {
return new Point((int)(Math.random()*250),
(int)(Math.random()*250));
}
}
6.4画弧:弧是唯一的一种非封闭的、但可以填充的图形
绘制方法:void drawArc(int x,int y,int w,int h,int startAngle,int endAngle)
void fillArc(int x,int y,int w,int h,int startAngle,int endAngle)
参数意义:x,y,w(宽度)y(高度)弧指定坐标路径。最后两个是弧的开始角度和结束角度
简单例子:
import java.applet.Applet;
import java.awt.*;
public class DrawArc extends Applet {
public void paint(Graphics g) {
g.setColor(Color.blue);
g.drawArc(10,10,150,100,0,210);//宽度和高度分别是151和101个像素,知道为什么呢吧?
g.setColor(Color.yellow);
g.fillArc(50,50,100,200,90,270);//宽100,高200个像素,因为是填充。
}
}
6.5绘制椭圆(not 圆角矩形)
绘制方法://宽高相同,所绘制的图形是一个圆
void drawOval(int x,int y,int w,int h)
void fillOval(int x,int y,int w,int h)
参数意义:外框与椭圆相内切的矩形框
聪明的你一定知道drawOval()所绘画的椭圆,适合的矩形是w+1个像素宽和h+1个像素高
6.6绘制多边形
绘制方法://与折线类似,另:初始点和结束点不是同一个点多边形将自动闭合
void drawPloygon(int[] xPoints,int[] yPoints,int[] numPoints)
void drawPolygon(Polygon polygon)
void fillPloygon(int xPoints,int[] yPoints,int[] numPoints)
void fillPolygon(Polygon polygon)
注意:尽管在AWT中提供非图形Polygon和Rectangle类,但Graphics类不提供
drawRect(Rectangle)方法,尽管存在一个drawPolygon(Polygon)方法。
6.7绘制文本
绘制方法:
void drawString(String s,int x,int y)
void drawChars(char[],int offset,int length,int x,int y)
void drawBytes(byte[],int offset,int length,int x,int y)
参数注意:所绘制文本的x,y位置对应的是文本基线,不是文本的左上角,和矩形不一样
可参看windows的SDK编程。JDK与windows的SDK编程很相似,同时学习可互为注脚,互相提高。
简单的一句话:字符以其底线为始,如A以大约左脚处为始。
技巧3:
如果字符串和矩形被绘制在同一个位置,则字符串将显示在矩形的上方
offset和length参数分别用来规定开始位置在数组中的偏移量和绘制的字符数,(Graphics类不提供旋转文本的能力)
6.8转换坐标系原点:如果不指定,则在Graphics坐标系中,原点一般设置在左上角
转换坐标方法:Graphics.translate()
两个整数参数值:描绘在原先的坐标系统中的点,在转换后的坐标系统中将成为新的原点
转换坐标的原因:一个原因是容器中没有滚动条而又要滚动其内容,如下例便是(点击鼠标可以滚动该图形)
转换坐标系的例子:
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
public class TranslateTest extends Applet {
Image image;
Point pressed = new Point(), lastTranslate = new Point();
public void init() {
image = getImage(getCodeBase(), "Rabbit.gif");//在IE缓存中随便找一个.gif文件即可,
try {
MediaTracker mt = new MediaTracker(this);
mt.addImage(image, 0);
mt.waitForID(0);
}
catch(InterruptedException e) {
e.printStackTrace();
}
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
java.awt.Point loc = e.getPoint();
file://Point loc = e.getPoint();//是原版,但编译时说
file://--TranslateTest.java:19: incompatible t
file://--found : java.awt.Point
file://--required: Point
file://--Point loc = e.getPoint();
file://-----故加了java.awt.于前。什么原因呢?THINK IN JAVA中说Point有几个类(awt中有,2D中有)。故...
// adjust mouse pressed location for
// translation ...
pressed.x = loc.x - lastTranslate.x;
pressed.y = loc.y - lastTranslate.y;
}
});
addMouseMotionListener(new MouseMotionAdapter() {
public void mouseDragged(MouseEvent e) {
java.awt.Point loc = e.getPoint();//加了java.awt
Point translate = new Point(loc.x - pressed.x,
loc.y - pressed.y);
Graphics g = getGraphics();
try {
g.clearRect(0,0,
getSize().width,getSize().height);
g.translate(translate.x, translate.y);
showStatus("Translating Graphics: " +
translate);
g.drawImage(image, 0, 0, TranslateTest.this);
}
finally {
g.dispose();
}
lastTranslate = translate;
}
});
}
public void paint(Graphics g) {
g.drawImage(image, 0, 0, this);
}
}
6.9剪贴:每个Graphics都有一个关联的剪贴矩形。剪贴矩形之所以这样命名是因为它们复制它们表示的矩形。另外,
随着Java 2D API的发展,剪贴区域的开关可以被设置为任意的开关,而不只局限于矩形
绘制方法:同表3-3
void setClip(int x,int y,int w,int) file://设置所要剪贴的区域是一个矩形区域
void setClip(Shape) file://设置所要剪贴的区域是任意形状
Rectangle getClipBounds() file://返回剪贴区域是一个矩形区域
Shape getClip() file://返回剪贴区域是任意形状
void clipRect(int x,int y,int w,int h) file://将剪贴矩形设置为当前剪贴矩形和方法中变无指定的矩形的交集
本节例子请参看例2。
技巧4:《向Component.paint()传递被剪贴的Graphics》
对于AWT的初学者来讲,经常向paint方法传递那些剪贴矩形小于构件的Graphics。结果,底层覆盖的paint方法不干
扰它接受的Graphics的剪贴矩形。当paint()绘制构件的内容时,着色操作仍被执行。在一些实例(如双缓冲和动画设计)
中,以只更新被剪贴的区域代替绘制完整的内容和依靠剪贴修复被损坏的区域,可以得到更好的效果
6.10图形模式:参看表3-3
XOR模式最一般的用途是在现有的图形上使用橡皮带生成法(在制图程序中和选择多样的对象时是一种很普通的用法)
面所有的例子都被设置成paint模式,所以在本节中我们将重点讲述XOR模式
文档中对XOR模式做了如下的描述的:Graphics.setXORMode(Color)
When drawing operations are performed,pixels which are the current color are changed to the
specify color,and vice versa。Pixels that are of colors other than those two color are changed
in an unpredictable but reversible manner;if the figure is drawn twice,then all pixels are
restored to their original values。
第二句中说明:如果在XOR模式连续两次执行复制图像操作,其下面的图形是不受影响的
使用XOR模式橡皮带生成法的一个例子:在一个图像上拉,生成一个矩形
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
public class xortest extends Applet {
java.awt.Point pressed, last;
Image image;
boolean firstRect;
public void init() {
image = getImage(getCodeBase(), "Rabbit.gif");
try {
MediaTracker mt = new MediaTracker(this);
mt.addImage(image, 0);
mt.waitForID(0);
}
catch(InterruptedException e) {
e.printStackTrace();
}
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
firstRect = true;
pressed = e.getPoint();
}
public void mouseReleased(MouseEvent e) {
if(pressed != null) {
java.awt.Point released = e.getPoint();
Rectangle clip = new Rectangle();
Graphics g = getGraphics();
Dimension size = getSize();
try {
clip.x = pressed.x;
clip.y = pressed.y;
clip.width =Math.abs(released.x - pressed.x);
clip.height =Math.abs(released.y - pressed.y);
g.clearRect(0,0,size.width,size.height);
g.setClip(clip);
g.drawImage(image, 0, 0, xortest.this);
}
finally {
g.dispose();
}
}
}
public void mouseClicked(MouseEvent e) {
repaint();
}
});
addMouseMotionListener(new MouseMotionAdapter() {
public void mouseDragged(MouseEvent e) {
java.awt.Point loc = e.getPoint();
Graphics g = getGraphics();
try {
g.setXORMode(getBackground());
if(firstRect) {
firstRect = false;
}
else {
g.drawRect(pressed.x, pressed.y,Math.abs(pressed.x - last.x),Math.abs(pressed.y - last.y));
}
g.drawRect(pressed.x, pressed.y,Math.abs(loc.x - pressed.x),Math.abs(loc.y - pressed.y));
last = e.getPoint();
}
finally {
g.dispose();
}
}
});
}
public void paint(Graphics g) {
g.drawImage(image, 0, 0, this);
}
}
6.11创建图形
当实现传递Graphics引用的方法时,最好是保证方法的结果不会引起Graphics的变化。换句话说,当方法返回时,
Graphics和调用前的情况应当是相同的。在该规则中当然也有例外,我们可以相当有把握地说,当调用paint()返
回时,paint(Graphics)的调用者仅仅处理Graphics。因此,可以改变传递给paint()的Graphics,而忽视维持
它的初始状态。但是,在另外的情况下,是否Graphics一定保留它的初始状态并没这么清楚。在这样的情况下,
最好是接受安全的步骤并保证Graphics不被改变,有下面的两个方法可以实现。
方法之一是:Graphics的所有的初始的特征可以被存储在本地,然后在方法返回之前重新设置:
file://code fragment
public void notSurelfGraphicsShouldChangeState(Graphics g){
Color oldColor=g.getColor();
Font oldFont=g.getFont();
file://modify g's color and font and perform graphical operations
g.setColor(oldColor);//restore old color
g.setfont(oldFont)//restore old font
很明显,一两个属性更改无所谓,大量的属性被修改后并被重新恢复就麻烦了
方法之二:那么创建Graphics的一个副本并用它代替传递给方法的Graphics是更方便的办法
public void notSurelfGraphicsShouldChangeState(Graphics g){
Graphics copy=g.create();
try{
file://use copy for rendering
}
finally{
g.dispose();//crucial
}
}
对于4.Graphics引用的寿命一节中的例子,该applet程序保留一个Graphics引用,由于传递
给paint()的Graphics是由调用者来进行处理的,所以Graphics引用仅在调用paint()期间有效
现在我们创建一个Graphics的副本,该副本在调用paint()之后将不被处理,如下面
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
public class HoldRef2 extends Applet {
private Graphics copy;
private boolean first = true;
public void paint(Graphics g) {
if(first) {
// note: copy is never disposed off
file://致命错误,即没有处理Graphics的副本
file://在实际中,许可制造出一个类成员的副本引用,并在另一个方法中在适当电动机及时处理它
copy = g.create();
copy.setColor(Color.red);
copy.setFont(new Font("Times Roman", Font.BOLD, 14));
first = false;
}
copy.drawString("Red Text", 10, 10);
}
}
创建Graphics的方法:
Graphics create()//创建的是一个精确的Graphics副本
Graphics create(int x,int y,int w,int h)//也是个副本,但返回的Graphics的原点被转换为
(x,y)坐标,剪贴矩形转换为原剪贴矩形和指定矩形的交集
例子:
import java.applet.Applet;
import java.awt.*;
public class CreateTest extends Applet {
private Image image;
public void init() {
MediaTracker mt = new MediaTracker(this);
image = getImage(getCodeBase(), "Rabbit.gif");
try {
mt.addImage(image, 0);
mt.waitForID(0);
}
catch(Exception e) {
e.printStackTrace();
}
}
public void paint(Graphics g) {
Graphics copy = g.create(image.getWidth(this),0,100,100);
try {
System.out.println("g: " + g.getClip().toString());
System.out.println("copy: " +copy.getClip().toString());
g.drawImage(image, 0, 0, this);
copy.drawImage(image, 0, 0, this);
}
finally {
copy.dispose();
}
}
}
加载图像(参见第5章“加载和显示图像”)并通过applet的paint方法创建一个要传递的Graphics的副本,
副本的原点变换到(imw,0),imw是指图形的宽度
副本也有它的剪贴矩形,设置为初始Graphics的剪贴矩形和由(image.getWidth(this),0,100,100)指定
的矩形的交集。因为初始Graphics的剪贴矩形覆盖由applet程序占领的区域,所以两个矩形的交集由
(image.getWidth(this),0,100,100)决定。
提示:复制的Graphics已经被转换,因为调用drawImage()在(0,0)绘制图像
啊!快结束了!略结一下:
(1)图形操作的坐标系被设定在设备的左上角,x和y轴的增长方向分别是向下和向右,坐标位于像素之间,
Graphics方法中绘制形状外形是通过设定图形的坐标而不是像素。图形笔在坐标路径右边和下边移动,
因此形状的外形导致在图形右边和下边各有一个额外
的像素行。在另一方面,形状填充图形内部,填充的尺寸和坐标路径一样
(2)AWT不提供支持特殊形状的类,如Line和Circle类.java.awt.Graphics提供大量的方法绘制和
填充图形、绘制文本、设置图形参数.但有限制,故要深入请学Java 2D API
(3)每个Graphics联系一个本机潜在的窗口系统中的图形环境。因此,Graphics描述的是必须被手工处理的
有限资源。如果通过调用一个方法返回一个Graphics引用以得到一个Graphics,则Graphics.dispose()
必须被调用为Graphics释放系统资源。另一方面,传递Graphics引用的方法通常不必处理Graphics。
一般地,这些方法调用者负责处理Graphics