SMTH Java FAQ (4) 2D作圖以及文字處理
1 如何在2D Graphics中使用雙緩衝?
首先你需要知道的是,如果你是在一個Swing組件上作圖的畫,Swing已經自動的爲你
實現了雙緩衝,因此沒有必要在這個問題上上腦筋了。//感謝上帝
如果你使用的是AWT組件的話,你可以根據下面的流程使用雙緩衝:
利用該組件之createImage()方法創建一個BufferedImage對象
利用BufferedImage的createGraphics()方法得到適當的Graphics對象
在得到的Graphics對象上作圖
將做出的圖形顯示到該組件之Graphics對象上。
下面的代碼片斷實現了上述流程,其中g 是圖形顯示組件之Graphics對象。
BufferedImage bi = (BufferedImage)createImage(w, h);
Graphics big = bi.createGraphics();
// draw somthing
g.drawImage(bi, 0, 0, this);
關於雙緩衝技術的詳細內容,可以參考Java Tutorial中的2D Graphics。
2 如何獲得一個字符的寬度和高度?
可以利用FontMetrics對象所提供的getHeight(), charWidth()或者是stringWidth()
方法來獲得顯示某個字符或者是字符串所需要的高度和寬度。例如,在Graphics g中:
String Info = "Test String".
FontMetrics metrics = getFontMetrics(g.getFont());
int height = metrics.getHeight();
int width = metrics.stringWidth(Info);
關於這些方法的具體情況,請參考FontMetrics類的API文檔。
3. 如何加載並顯示較大的圖形文件?
在The Java Tutorial -> Creating a GUI with JFC/Swing中,我們看到加載並在一
個Graphics對象上顯示一個圖形文件的步驟如下:
Toolkit toolkit = Toolkit.getDefaultToolkit();
Image image1 = toolkit.getImage("imageFile.gif");
g.drawImage(myImage, 0, 0, this);
當你所需要顯示的圖形文件不大的時候,這樣的做法似乎沒有什麼問題。但是,當你
第一次顯示一幅很大的圖形時,是不是看見你的圖形界面閃爍了好久才顯示出圖形來
呢?很鬱悶是吧?Java 2D Graphics不是缺省的是用雙緩衝技術麼?爲什麼還會出現
這種閃爍呢?
造成這種現象的原因是:Image 對象的家載是通過一個線程來進行的,當虛擬機剛剛
執行完toolkit.getImage()語句的時候,新創建的Image 對象實際上還在不斷的更新
之中,如果這時候調用g.drawImage()來顯示該圖形,那麼該Image對象每更新一次,
系統就將該圖形重新畫一次,這就是爲什麼圖形界面會出現閃爍的原因。
那麼,我先做點別的事情,然後再來顯示這個圖形,總該可以了吧。試試下面這一段
程序怎麼樣?
Toolkit toolkit = Toolkit.getDefaultToolkit();
Image image1 = toolkit.getImage("imageFile.gif");
{
// Do something for 10 seconds
}
g.drawImage(myImage, 0, 0, this);
呵呵,似乎效果還是不行耶。即使是你在那裏灌水灌到被封全站3 個月,這個討厭的
閃爍還是在哪裏。原因很簡單,負責加載圖形的是一個低優先權線程,你在那裏佔用
着CPU 時間,它怎麼可能會幹活呢?你在那裏覺覺10秒鐘Thread.sleep()試試看,是
不是乖好多了呢?
很顯然,我們不能夠每加載一個圖形文件就在那裏覺覺10秒鐘,不然的話飯碗早就丟
掉了。我們需要做的,是等待圖像加載完成,這個加載一完成就得馬上開始幹活。因
此,我們需要一個waitForImage()方法。一個可用的waitForImage()方法如下,其中
的Component可以是你的JFrame對象,把你的覺覺10秒鐘換成waitForImage() 就可以
向老闆請求加薪啦。
public static boolean waitForImage(Image image, Component c)
{
MediaTracker tracker = new MediaTrracker(c);
tracker.addImage(image, 0);
try
{
tracker.waitForAll();
} catch(InterruptedException e) {}
return (!tracker.isErrorAny());
}
如果你願意使用最少的代碼實現還算不賴的效果,那麼下面的代碼斷也可以滿足你的
要求。至於這裏面的原因嘛,偶現在打字比較慢,你自己好好想想吧。
Toolkit toolkit = Toolkit.getDefaultToolkit();
Image image1 = toolkit.getImage("imageFile.gif");
JLabel NullLabel = new JLabel(new ImageIcon(image1));
g.drawImage(myImage, 0, 0, this);
4 能夠在非中文平臺上顯示漢字嗎?
Java程序能夠使用操作系統所提供的字體,因此只要你的操作系統上安裝有一個支持
中文的字體你就可以在你的程序中顯示漢字。爲了達到這個目的,首先你必須創建一
個Font對象,並且指定Graphics對象使用該字體,具體做法如下:
String ChineseInfo = "中文信息";
Font SysFont = new Font("FangSong_GB2312", Font.TRUETYPE_FONT, 16);
Graphics2D g2 = (Graphics2D) g;
g2.setFont(SysFont);
g2.drawString(ChineseInfo, 10, 10);
在上面的示範程序中,"FangSong_GB2312" 是操作系統上支持中文的一個字體名稱。
這是中文Windows 9x/2000/NT操作系統缺省提供的一箇中文字體,與系統字體目錄下
的simfang.ttf相對應。如果將該字體安裝到非中文的Windows 9x/2000/NT 操作系統
上,則利用上述程序片斷也可以正確的顯示中文。
需要說明的是,由於上述程序片斷中含有中文信息,在非中文平臺上進行編譯的時候
需要指定encoding選項。
5 如何在非中文平臺上安裝中文字體
這本來不是一個Java問題,不過跟上面的中文顯示問題相關。下面僅就真字體的安裝
做一個簡單說明:
5.1 Windows 9x/2000/NT平臺
在Windows 9x/2000/NT平臺上,開始菜單 -> 配置 -> 控制面版,在控制面板中選擇
“字體”,在文件菜單中選擇“安裝新字體”,進入字體文件所在的目錄,選擇所有
的字體並點擊“確認”按鈕即可。
5.2 Linux 平臺
爲了在Linux 平臺上安裝並啓用真字體,你需要擁有root權限。將所有字體文件拷貝
到/usr/share/fonts/default/TrueType目錄下。運行如下兩個命令:
ttmkfdir > fonts.dir
cat fonts.dir > fonts.scale
改寫/etc/X11/XF86Config-4以包含如下信息:
Section "FontPath"
... ...
FontPath "/usr/share/fonts/default/TrueType"
EndSection
Section "Module"
... ...
Load "xtt"
EndSection
重新啓動機器或者是字體服務器。在有的平臺上,你也許需要關閉xfs服務器。
5.3 Solaris 平臺
運行/usr/dt/bin/sdtfontadm進入字體管理圖形用戶界面。在字體菜單中選擇安裝,
在隨後出現的對話框中選擇真字體文件所在的目錄,然後選擇需要安裝的字體。安裝
該字體後確認自動將安裝目錄加入到FontPath中即可。
6 上哪裏去找支持中文的字體?
如果你有一箇中文的Windows 9x/2000/NT操作系統,那麼你的Java程序就可以在非中
文的Windows 9x/2000/NT, Linux, Solaris系統上顯示中文了。在Windows/Fonts 目
錄下找到如下文件並按照上面的提示將需要字體安裝到目標平臺上即可。
simhei.ttf -- 黑體 (SimHei)
simfang.ttf -- 仿宋體 (FangSong_GB2312)
simkai.ttf -- 楷體 (KaiTi_GB2312)
simsun.ttf -- 宋體 (SimSun, NSimSun)
mssong.ttf -- 宋體 (MS Song,不建議使用)
7 如何使畫出來的文字具有不一樣的背景色?
目前Java的drawString()方法缺省地使用當前Graphics對象的背景顏色作爲文字的背
景顏色,並且不提供專門的方法來改變這種設置。因此,我們不能夠直接畫出具有不
一樣背景的文字。一個可行的辦法是獲得文字信息的高度和寬度,在適當的位置先畫
出背景色,然後再同樣的位置畫出文字。
一段實現了這個方法的程序如下:
Color BackColor = Color.red, ForeColor = Color.blue;
int CurrX = 10, CurrY = 10;
String Info = "Test String".
FontMetrics metrics = getFontMetrics(g.getFont());
int height = metrics.getHeight();
int width = metrics.stringWidth(Info);
g2.setPaint(BackColor);
g2.fillRect(CurrX, CurrY - height, width, height);
g2.setPaint(ForeColor);
g2.drawString(CurrX, CurrY);
8 如何在作圖區域接受鍵盤輸入?
一個可行的辦法是利用KeyListener 監測鍵盤活動。當檢測到鍵盤活動的時候獲得相
對應的字符並顯示到作圖區域合適的位置。爲了達到這個目的,我們需要定義自己的
keyPressed()事件,例如:
int CurrX, CurrY;
public void keyPressed(KeyEvent e)
{
char c = e.getKeyChar();
g.drawString("" + c, CurrX, CurrY)
}
需要說明的是,利用這個方法無法向作圖區域輸入中文。如果有哪位網友找到了輸入
中文的方法,請指教。
9 如何實現陰影、鏤空、離散等特種文字效果?
下面是從http://www.javaworld.com/javaworld/javatips/jw-javatip81.html 得到
的一些實現特種文字效果的代碼片斷。感興趣的網友可以自己去看原文。
// Shadow
g.setColor(new Color(50, 50, 50));
g.drawString("Shadow", ShiftEast(x, 2), ShiftSouth(y, 2));
g.setColor(new Color(220, 220, 220));
g.drawString("Shadow", x, y);
// Engrave
g.setColor(new Color(220, 220, 220));
g.drawString("Engrave", ShiftEast(x, 1), ShiftSouth(y, 1));
g.setColor(new Color(50, 50, 50));
g.drawString("Engrave", x, y);
file://Outline
g.setColor(Color.red);
g.drawString("Outline", ShiftWest(x, 1), ShiftNorth(y, 1));
g.drawString("Outline", ShiftWest(x, 1), ShiftSouth(y, 1));
g.drawString("Outline", ShiftEast(x, 1), ShiftNorth(y, 1));
g.drawString("Outline", ShiftEast(x, 1), ShiftSouth(y, 1));
g.setColor(Color.yellow);
g.drawString("Outline", x, y);
file://Hollow
g.setColor(Color.black);
g.drawString("Hollow", ShiftWest(x, 1), ShiftNorth(y, 1));
g.drawString("Hollow", ShiftWest(x, 1), ShiftSouth(y, 1));
g.drawString("Hollow", ShiftEast(x, 1), ShiftNorth(y, 1));
g.drawString("Hollow", ShiftEast(x, 1), ShiftSouth(y, 1));
g.setColor(bg);
g.drawString("Hollow", x, y);
file://Segment
int w = (g.getFontMetrics()).stringWidth("Segment");
int h = (g.getFontMetrics()).getHeight();
int d = (g.getFontMetrics()).getDescent();
g.setColor(new Color(220, 220, 220));
g.drawString("Segment", x, y);
g.setColor(bg);
for (int i = 0; i < h; i += 3)
g.drawLine(x, y + d - i, x + w, y + d - i);
file://3D Effects
Color top_color = new Color(200, 200, 0);
Color side_color = new Color(100, 100, 0);
for (int i = 0; i < 5; i++)
{
g.setColor(top_color);
g.drawString("3-Dimension", ShiftEast(x, i), ShiftNorth(ShiftSouth(y, i), 1));
g.setColor(side_color);
g.drawString("3-Dimension", ShiftWest(ShiftEast(x, i), 1), ShiftSouth(y, ii));
}
g.setColor(Color.yellow);
g.drawString("3-Dimension", ShiftEast(x, 5), ShiftSouth(y, 5));
file://Motion
for (int i = 0; i < 20; i++)
{
font_size = 12 + i;
g.setFont(new Font("TimesRoman", Font.PLAIN, font_size));
w = (g.getFontMetrics()).stringWidth("Motion");
g.setColor(new Color(0, 65 + i * 10, 0));
g.drawString("Motion", (width - w) / 2, ShiftSouth(y, speed * i));
}
首先你需要知道的是,如果你是在一個Swing組件上作圖的畫,Swing已經自動的爲你
實現了雙緩衝,因此沒有必要在這個問題上上腦筋了。//感謝上帝
如果你使用的是AWT組件的話,你可以根據下面的流程使用雙緩衝:
利用該組件之createImage()方法創建一個BufferedImage對象
利用BufferedImage的createGraphics()方法得到適當的Graphics對象
在得到的Graphics對象上作圖
將做出的圖形顯示到該組件之Graphics對象上。
下面的代碼片斷實現了上述流程,其中g 是圖形顯示組件之Graphics對象。
BufferedImage bi = (BufferedImage)createImage(w, h);
Graphics big = bi.createGraphics();
// draw somthing
g.drawImage(bi, 0, 0, this);
關於雙緩衝技術的詳細內容,可以參考Java Tutorial中的2D Graphics。
2 如何獲得一個字符的寬度和高度?
可以利用FontMetrics對象所提供的getHeight(), charWidth()或者是stringWidth()
方法來獲得顯示某個字符或者是字符串所需要的高度和寬度。例如,在Graphics g中:
String Info = "Test String".
FontMetrics metrics = getFontMetrics(g.getFont());
int height = metrics.getHeight();
int width = metrics.stringWidth(Info);
關於這些方法的具體情況,請參考FontMetrics類的API文檔。
3. 如何加載並顯示較大的圖形文件?
在The Java Tutorial -> Creating a GUI with JFC/Swing中,我們看到加載並在一
個Graphics對象上顯示一個圖形文件的步驟如下:
Toolkit toolkit = Toolkit.getDefaultToolkit();
Image image1 = toolkit.getImage("imageFile.gif");
g.drawImage(myImage, 0, 0, this);
當你所需要顯示的圖形文件不大的時候,這樣的做法似乎沒有什麼問題。但是,當你
第一次顯示一幅很大的圖形時,是不是看見你的圖形界面閃爍了好久才顯示出圖形來
呢?很鬱悶是吧?Java 2D Graphics不是缺省的是用雙緩衝技術麼?爲什麼還會出現
這種閃爍呢?
造成這種現象的原因是:Image 對象的家載是通過一個線程來進行的,當虛擬機剛剛
執行完toolkit.getImage()語句的時候,新創建的Image 對象實際上還在不斷的更新
之中,如果這時候調用g.drawImage()來顯示該圖形,那麼該Image對象每更新一次,
系統就將該圖形重新畫一次,這就是爲什麼圖形界面會出現閃爍的原因。
那麼,我先做點別的事情,然後再來顯示這個圖形,總該可以了吧。試試下面這一段
程序怎麼樣?
Toolkit toolkit = Toolkit.getDefaultToolkit();
Image image1 = toolkit.getImage("imageFile.gif");
{
// Do something for 10 seconds
}
g.drawImage(myImage, 0, 0, this);
呵呵,似乎效果還是不行耶。即使是你在那裏灌水灌到被封全站3 個月,這個討厭的
閃爍還是在哪裏。原因很簡單,負責加載圖形的是一個低優先權線程,你在那裏佔用
着CPU 時間,它怎麼可能會幹活呢?你在那裏覺覺10秒鐘Thread.sleep()試試看,是
不是乖好多了呢?
很顯然,我們不能夠每加載一個圖形文件就在那裏覺覺10秒鐘,不然的話飯碗早就丟
掉了。我們需要做的,是等待圖像加載完成,這個加載一完成就得馬上開始幹活。因
此,我們需要一個waitForImage()方法。一個可用的waitForImage()方法如下,其中
的Component可以是你的JFrame對象,把你的覺覺10秒鐘換成waitForImage() 就可以
向老闆請求加薪啦。
public static boolean waitForImage(Image image, Component c)
{
MediaTracker tracker = new MediaTrracker(c);
tracker.addImage(image, 0);
try
{
tracker.waitForAll();
} catch(InterruptedException e) {}
return (!tracker.isErrorAny());
}
如果你願意使用最少的代碼實現還算不賴的效果,那麼下面的代碼斷也可以滿足你的
要求。至於這裏面的原因嘛,偶現在打字比較慢,你自己好好想想吧。
Toolkit toolkit = Toolkit.getDefaultToolkit();
Image image1 = toolkit.getImage("imageFile.gif");
JLabel NullLabel = new JLabel(new ImageIcon(image1));
g.drawImage(myImage, 0, 0, this);
4 能夠在非中文平臺上顯示漢字嗎?
Java程序能夠使用操作系統所提供的字體,因此只要你的操作系統上安裝有一個支持
中文的字體你就可以在你的程序中顯示漢字。爲了達到這個目的,首先你必須創建一
個Font對象,並且指定Graphics對象使用該字體,具體做法如下:
String ChineseInfo = "中文信息";
Font SysFont = new Font("FangSong_GB2312", Font.TRUETYPE_FONT, 16);
Graphics2D g2 = (Graphics2D) g;
g2.setFont(SysFont);
g2.drawString(ChineseInfo, 10, 10);
在上面的示範程序中,"FangSong_GB2312" 是操作系統上支持中文的一個字體名稱。
這是中文Windows 9x/2000/NT操作系統缺省提供的一箇中文字體,與系統字體目錄下
的simfang.ttf相對應。如果將該字體安裝到非中文的Windows 9x/2000/NT 操作系統
上,則利用上述程序片斷也可以正確的顯示中文。
需要說明的是,由於上述程序片斷中含有中文信息,在非中文平臺上進行編譯的時候
需要指定encoding選項。
5 如何在非中文平臺上安裝中文字體
這本來不是一個Java問題,不過跟上面的中文顯示問題相關。下面僅就真字體的安裝
做一個簡單說明:
5.1 Windows 9x/2000/NT平臺
在Windows 9x/2000/NT平臺上,開始菜單 -> 配置 -> 控制面版,在控制面板中選擇
“字體”,在文件菜單中選擇“安裝新字體”,進入字體文件所在的目錄,選擇所有
的字體並點擊“確認”按鈕即可。
5.2 Linux 平臺
爲了在Linux 平臺上安裝並啓用真字體,你需要擁有root權限。將所有字體文件拷貝
到/usr/share/fonts/default/TrueType目錄下。運行如下兩個命令:
ttmkfdir > fonts.dir
cat fonts.dir > fonts.scale
改寫/etc/X11/XF86Config-4以包含如下信息:
Section "FontPath"
... ...
FontPath "/usr/share/fonts/default/TrueType"
EndSection
Section "Module"
... ...
Load "xtt"
EndSection
重新啓動機器或者是字體服務器。在有的平臺上,你也許需要關閉xfs服務器。
5.3 Solaris 平臺
運行/usr/dt/bin/sdtfontadm進入字體管理圖形用戶界面。在字體菜單中選擇安裝,
在隨後出現的對話框中選擇真字體文件所在的目錄,然後選擇需要安裝的字體。安裝
該字體後確認自動將安裝目錄加入到FontPath中即可。
6 上哪裏去找支持中文的字體?
如果你有一箇中文的Windows 9x/2000/NT操作系統,那麼你的Java程序就可以在非中
文的Windows 9x/2000/NT, Linux, Solaris系統上顯示中文了。在Windows/Fonts 目
錄下找到如下文件並按照上面的提示將需要字體安裝到目標平臺上即可。
simhei.ttf -- 黑體 (SimHei)
simfang.ttf -- 仿宋體 (FangSong_GB2312)
simkai.ttf -- 楷體 (KaiTi_GB2312)
simsun.ttf -- 宋體 (SimSun, NSimSun)
mssong.ttf -- 宋體 (MS Song,不建議使用)
7 如何使畫出來的文字具有不一樣的背景色?
目前Java的drawString()方法缺省地使用當前Graphics對象的背景顏色作爲文字的背
景顏色,並且不提供專門的方法來改變這種設置。因此,我們不能夠直接畫出具有不
一樣背景的文字。一個可行的辦法是獲得文字信息的高度和寬度,在適當的位置先畫
出背景色,然後再同樣的位置畫出文字。
一段實現了這個方法的程序如下:
Color BackColor = Color.red, ForeColor = Color.blue;
int CurrX = 10, CurrY = 10;
String Info = "Test String".
FontMetrics metrics = getFontMetrics(g.getFont());
int height = metrics.getHeight();
int width = metrics.stringWidth(Info);
g2.setPaint(BackColor);
g2.fillRect(CurrX, CurrY - height, width, height);
g2.setPaint(ForeColor);
g2.drawString(CurrX, CurrY);
8 如何在作圖區域接受鍵盤輸入?
一個可行的辦法是利用KeyListener 監測鍵盤活動。當檢測到鍵盤活動的時候獲得相
對應的字符並顯示到作圖區域合適的位置。爲了達到這個目的,我們需要定義自己的
keyPressed()事件,例如:
int CurrX, CurrY;
public void keyPressed(KeyEvent e)
{
char c = e.getKeyChar();
g.drawString("" + c, CurrX, CurrY)
}
需要說明的是,利用這個方法無法向作圖區域輸入中文。如果有哪位網友找到了輸入
中文的方法,請指教。
9 如何實現陰影、鏤空、離散等特種文字效果?
下面是從http://www.javaworld.com/javaworld/javatips/jw-javatip81.html 得到
的一些實現特種文字效果的代碼片斷。感興趣的網友可以自己去看原文。
// Shadow
g.setColor(new Color(50, 50, 50));
g.drawString("Shadow", ShiftEast(x, 2), ShiftSouth(y, 2));
g.setColor(new Color(220, 220, 220));
g.drawString("Shadow", x, y);
// Engrave
g.setColor(new Color(220, 220, 220));
g.drawString("Engrave", ShiftEast(x, 1), ShiftSouth(y, 1));
g.setColor(new Color(50, 50, 50));
g.drawString("Engrave", x, y);
file://Outline
g.setColor(Color.red);
g.drawString("Outline", ShiftWest(x, 1), ShiftNorth(y, 1));
g.drawString("Outline", ShiftWest(x, 1), ShiftSouth(y, 1));
g.drawString("Outline", ShiftEast(x, 1), ShiftNorth(y, 1));
g.drawString("Outline", ShiftEast(x, 1), ShiftSouth(y, 1));
g.setColor(Color.yellow);
g.drawString("Outline", x, y);
file://Hollow
g.setColor(Color.black);
g.drawString("Hollow", ShiftWest(x, 1), ShiftNorth(y, 1));
g.drawString("Hollow", ShiftWest(x, 1), ShiftSouth(y, 1));
g.drawString("Hollow", ShiftEast(x, 1), ShiftNorth(y, 1));
g.drawString("Hollow", ShiftEast(x, 1), ShiftSouth(y, 1));
g.setColor(bg);
g.drawString("Hollow", x, y);
file://Segment
int w = (g.getFontMetrics()).stringWidth("Segment");
int h = (g.getFontMetrics()).getHeight();
int d = (g.getFontMetrics()).getDescent();
g.setColor(new Color(220, 220, 220));
g.drawString("Segment", x, y);
g.setColor(bg);
for (int i = 0; i < h; i += 3)
g.drawLine(x, y + d - i, x + w, y + d - i);
file://3D Effects
Color top_color = new Color(200, 200, 0);
Color side_color = new Color(100, 100, 0);
for (int i = 0; i < 5; i++)
{
g.setColor(top_color);
g.drawString("3-Dimension", ShiftEast(x, i), ShiftNorth(ShiftSouth(y, i), 1));
g.setColor(side_color);
g.drawString("3-Dimension", ShiftWest(ShiftEast(x, i), 1), ShiftSouth(y, ii));
}
g.setColor(Color.yellow);
g.drawString("3-Dimension", ShiftEast(x, 5), ShiftSouth(y, 5));
file://Motion
for (int i = 0; i < 20; i++)
{
font_size = 12 + i;
g.setFont(new Font("TimesRoman", Font.PLAIN, font_size));
w = (g.getFontMetrics()).stringWidth("Motion");
g.setColor(new Color(0, 65 + i * 10, 0));
g.drawString("Motion", (width - w) / 2, ShiftSouth(y, speed * i));
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.