學習筆記之JAVA圖形設計卷I AWT——第3章 圖 形 (轉)

amyz發表於2007-11-12
學習筆記之JAVA圖形設計卷I AWT——第3章 圖 形 (轉)[@more@]

 學習筆記之圖形設計卷I AWT——第3章 圖 形

前時顯示器壞了,前言:我覺得寫的不是學習筆記,倒象教程。我是想讓有所獲,故詳細了點。
注意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的引用的方法
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
   包     類         方 法
  ─────────────────────────────────
  java.awt   Canvas       paint(Graphics g)
         Component     paint(Graphics g)
         Component     paintAll(Graphics g)
         Component     print(Graphics g)
         Component     printAll(Graphics g)
         Component     update(Graphics g)
         Container     paint(Graphics g)
         Container     paintComponents(Graphics g)
         Container     print(Graphics g)
         Container     printComponents(Graphics g)
         ScrollPane     printComponents(Graphics g)
  java.beans  Property_Enditor paintValue(Graphics g,Rectangle r)
         Property_EnditorSupport paintValue(Graphics g,Rectangle r)
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

         表3-2 返回Graphics 引用的JDK方法
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
   軟體包     類         方 法
  ─────────────────────────────────
  java.awt  Component     getGraphics()---(為最常使用)
        Image       getGraphics()
        PaintJob      getGraphics()
        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 的一部分,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
}
}
便一個HTML如001.html,

Sample Applet




令行下輸入: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---&gtg.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();
}
}
} 麼一開始畫出的線就是黃色的,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{
something with g- if an exception is thrown,
finally block will be executed
}
finally{
g.dispose()//crucial至關重要,因為忽略不做將會導致視窗系統用完圖形環境,這在大部分操作上會引起問題
}
}
}
這裡要注意的是對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.ran()*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) {
arraySize = ((int)(Math.random()*100));//原例子
int arraySize =4;//線數過多,故我設為4
int[] xPoints = new int[arraySize];
int[] yPoints = new int[arraySize];//xPoints和yPoints要相等
[] 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:
程式中配備有三個按鈕和一個核取方塊,繪製矩形的程式碼被封裝在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 rounutton = 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.get())).getState();
}
});
}
制所有的矩形時,採用的都是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) {
制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位置對應的是文字基線,不是文字的左上角,和矩形不一樣
 可參看的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();
loc = e.getPoint();//是原版,但編譯時說
incompatible t
  : java.awt.Point
Point
loc = e.getPoint(); 
  ^
加了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) 置所要剪貼的區域是一個矩形區域
void setClip(Shape) 置所要剪貼的區域是任意形狀
Rectangle getClipBounds() 回剪貼區域是一個矩形區域
Shape getClip() 回剪貼區域是任意形狀
void clipRect(int x,int y,int w,int h) 剪貼矩形設定為當前剪貼矩形和方法中變無指定的矩形的交集
本節例子請參看例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 cl= 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的所有的初始的特徵可以被在本地,然後在方法返回之前重新設定:
fragment
public void notSurelfGraphicsShouldChangeState(Graphics g){
Color oldColor=g.getColor();
Font oldFont=g.getFont();
g's color and font and perfographical 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{
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
命錯誤,即沒有處理Graphics的副本
實際中,許可製造出一個類成員的副本引用,並在另一個方法中在適當電動機及時處理它
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


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752019/viewspace-981890/,如需轉載,請註明出處,否則將追究法律責任。

相關文章