国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

java指南之使用圖形:自定義繪圖概覽

2019-11-18 14:15:33
字體:
來源:轉載
供稿:網友

  自定義繪圖概覽
假如你還沒有讀過 繪圖 一節,請現在就看看。那節描述了Swing 組件是如何被繪制的 --假如你要寫自定義繪圖代碼,那些就是基礎知識。
在實現一個自定義繪圖的組件前首先請確認你真的需要這樣做。你可能可以使用 標簽, 按鈕或者 文本組件 的功能代替。記住,你可以使用 邊界自定義組件的外部邊界。

假如你真的需要進行自定義繪圖,那么就需要決定使用哪個超類。我們推薦要么擴展JPanel要么使用一個更非凡的Swing組件。例如,假如你想創建一個自定義按鈕,你也許應該通過擴展一個像JButton 或者 JToggleButton 這樣的按鈕類來實現它。用那個方法,你就可以繼續那些類提供的狀態治理功能。假如你正創建一個在圖像上繪圖的組件,你也許想創建一個JLabel 的子類。另一方面,假如你正實現一個在空白的或者透明的背景上產生和顯示圖表的組件,那么你可能想使用JPanel作為超類。

在實現自定義繪圖代碼的時候,記住兩件事:

你的繪圖代碼應該在一個名字為 paintComponent的方法里面。
你可以 -- 而且可能是應該 -- 使用一個邊界繪制你的組件的外邊緣。
自定義繪圖的一個范例
下面的代碼給出了一個自定義繪圖的范例。它將一個圖像顯示兩次,一次以圖像的原始大小一次非常寬。
class ImagePanel extends JPanel {
...
public void paintComponent(Graphics g) {
super.paintComponent(g); //paint background

//首先以圖像的原始大小顯示。
g.drawImage(image, 0, 0, this); //85x62 image

//現在顯示縮放的圖像。
g.drawImage(image, 90, 0, 300, 62, this);
}
}

下面是結果:


這個圖片是該applet的GUI。要運行那個applet,單擊圖片。該applet將在一個新瀏覽窗口顯示。

范例代碼來自 ImageDisplayer.java,它的更進一步的討論在 顯示圖像中。那個范例示范了在組件進行自定義繪圖時的一些新規則:

繪圖代碼執行一些標準的Swing組件所沒有的動作。假如我們只想將圖像以它的原始大小顯示一次,我們應該使用JLabel 對象而不是使用自定義組件。
自定義組件是JPanel的子類。這是自定義組件的一個常用的超類。
所有的自定義繪圖代碼都在paintComponent方法里面。
在執行任何自定義繪圖前,通過調用super.paintComponent讓組件繪制自己的背景。假如你沒有調用它,要么自己的代碼繪制組件的背景,要么對組件調用setOpaque(false)。使用后者將通知Swing繪圖系統在透明的組件后面的組件可能是可見的因此應該被繪制。
這個組件沒有考慮的一個事情就是邊界。它不僅沒有使用邊界,而且沒有調整它的繪圖坐標以考慮有邊界的情況。一個產品級的組件應該像下一小節描述的那樣為邊界進行調整。


坐標系統
每個組件都有自己的整型坐標系統,范圍從(0, 0) 到 (width - 1, height - 1),單位是象素。像下面的圖片顯示的那樣,組件的繪圖區的左上角是(0, 0)。X坐標向右增加而Y坐標向下增加。



在繪制一個組件時,你不僅要考慮組件的尺寸而且在需要時還要考慮組件的邊界的尺寸。例如組件四周繪制一個象素寬的邊界將左上角的坐標從(0,0)變成(1,1)而且將繪圖區的寬度和高度各減小2個象素(每邊一個象素)。下面的圖片說明了這個:




要得到組件的寬度和高度可以使用它的getWidth和 getHeight 方法。要得到邊界的尺寸,使用getInsets方法。下面是一個組件決定自定義繪圖區的可用寬度和高度的可能的代碼:
public void paintComponent(Graphics g) {
...
Insets insets = getInsets();
int currentWidth = getWidth() - insets.left - insets.right;
int currentHeight = getHeight() - insets.top - insets.bottom;
...
.../* 第一次繪圖發生在(x,y), x不能小于
insets.left, 而y不能小于insets.height。 */...
}

為了讓你自己熟悉坐標系統,你可以運行下面的applet。無論你在框架區域的任何地方點擊都將繪制一個點而且下面的標簽會顯示該點的坐標。假如你點擊邊界,點就不是很清楚,因為組件的邊界是在執行自定義繪圖后被繪制的。假如你不想要這個效果,一個簡單的解決方法是將組件的邊界從它移動到一個新建的包含該組件的JPanel對象上。




這是applet的GUI的一個截圖。要運行這個applet,單擊圖片。applet將在一個新窗口中

這個程序在 CoordinatesDemo.java中被實現。雖然這個范例代碼沒有在任何地方被討論,但是它和 RectangleDemo 程序的代碼很相似,那個程序將在稍后的 繪制外形中討論。
repaint方法的參數
記住調用組件的repaint 方法請求組件被預定去繪制自己。當繪圖系統不能跟上repaint請求的要求時(譯者注:主要是因為過于頻繁的調用repaint,例如在paint方法中調用repaint(1),每隔一毫秒重繪一次),它會將多個請求合并為一個。
repaint方法有兩個有用的形式:

void repaint()
請求整個組件被重繪。
void repaint(int, int, int, int)
請求組件中指定的區域被重繪。參數指定區域的左上角的X,Y坐標和區域的寬度和高度。
雖然使用四參數形式的repaint方法通常沒有實用價值,但是它可以顯著的提高繪圖性能。下面的圖片顯示的程序在頻繁請求繪制以顯示用戶當前選定區域時使用四參數的repaint方法。這樣做避免了繪制從上次的繪圖操作后沒有被改變的區域。



這是applet的GUI的一個截圖。要運行這個applet,單擊圖片。applet將在一個新窗口中

這個程序在 SelectionDemo.java中實現。下面是計算繪制區域并繪制它的代碼:
class SelectionArea extends JLabel {
...
public SelectionArea(ImageIcon image, ...) {
super(image); //使這個組件顯示一個圖像。
...
}

...//在一個鼠標拖動事件處理器中:
Rectangle totalRepaint = rectToDraw.union(PReviousRectDrawn);
repaint(totalRepaint.x, totalRepaint.y,
totalRepaint.width, totalRepaint.height);
...
public void paintComponent(Graphics g) {
super.paintComponent(g); //繪制背景和圖像
...
//在圖像上繪制一個矩形。
g.setColor(Color.white);
g.drawRect(rectToDraw.x, rectToDraw.y,
rectToDraw.width - 1, rectToDraw.height - 1);
...
}
...
}

就像你看到的,這個自定義組件擴展JLabel,因此它繼續了顯示圖像的能力。用戶可以通過拖動鼠標選定一個矩形區域。組件連續的顯示一個矩形以指出當前選定的尺寸。為了提高繪制速度,組件的鼠標拖動事件處理器為repaint指定一個繪制區域。
通過限制重繪的區域,事件處理器避免了不必要的重繪圖像外的區域。對于這個小圖像,這個策略沒有顯著的性能上的提高。但是對于一個巨大的圖像,這可能就有真正的好處了。并且假如是用從文件里面繪制圖像的情況代替,你還必須計算在矩形下面繪制什么--例如,在一個拖動程序中計算外形--然后使用繪制區域的知識限制你執行的計算可能會顯著的提升性能。

指定給repaint方法的區域不光包括要被繪制的區域,還有所有需要擦除的區域。否則原來被繪制的東西保持可見直到另外的繪制操作碰巧擦除了它。前面的代碼通過結合將被繪制的矩形和先前繪制的矩形來計算總的區域。

為repaint方法指定的繪制區域被反映到傳遞給paintComponent方法的Graphics對象中。你可以使用getClipBounds 方法決定哪個矩形區域被繪制。下面是一個使用剪切區域的例子:


public void paintComponent(Graphics g) {
Rectangle clipRect = g.getClipBounds();
if (clipRect != null) {
//假如它有效,只繪制由clipRect指定的區域。
//最左上角坐標為 (clipRect.x, clipRect.y)
//寬度,高度為 clipRect.width, clipRect.height
} else {
//繪制整個組件
}
}

Graphics對象
被傳遞到paintComponent方法的 Graphics 對象提供繪制環境和執行繪制的方法。那些方法將在稍后討論,他們有諸如 drawImage, drawString, drawRect和 fillRect這樣的名字。
圖形環境由諸如當前繪圖色,當前字體和當前繪制區(就像你已經見過的)這樣的狀態組成,顏色和字體被初始化為在調用paintComponent前的背景色和組件的字體。你可以使用getColor和getFont方法得到它們,用setColor 和 setFont方法設置它們。

假如你愿意,你可以安全的忽略當前的繪制區,這對組件的坐標系統沒有任何影響,任何區域外的繪圖被忽略。然而假如在繪圖區域減小時你的繪圖代碼包括可以被簡化的復雜的操作,那么你應該使用繪圖區的知識幫助提高繪圖的性能。就像前面的代碼顯示的那樣,你通過調用getClipBounds方法從Graphics 對象得到繪圖區的矩形范圍。

你可以使用兩種方法減小繪圖區。首先是在任何可能的情況下指定repaint 的參數。另一個就是實現paintComponent,讓它調用Graphics 對象的setClip 方法。假如你使用setClip,確保在返回前恢復原始的繪圖區。否則組件可能被不正確的繪制。下面是一個減小然后恢復繪圖區的例子:

Rectangle oldClipBounds = g.getClipBounds();
Rectangle clipBounds = new Rectangle(...);
g.setClip(clipBounds);

...//執行自定義繪制...

g.setClip(oldClipBounds);

在寫你的繪圖代碼時記住你不能依靠除了提供的Graphics對象外的任何圖形環境。例如你不能依靠你對repaint指定的繪圖區和隨后調用的paintComponent中的繪圖區一樣。一種情況是多個重繪請求可以被合并到一個paintComponent 調用,對應的繪圖區進行調整。另一種情況是繪圖系統有時候自己會調用 paintComponent方法,沒有從你的程序中調用任何重繪請求。一個例子是繪圖系統在第一次顯示組件的GUI時調用組件的paintComponent 方法,同樣當GUI被其它的窗口覆蓋而又出現時,繪圖系統調用paintComponent 方法繪制最近出現的部分。

Swing繪圖方法
paintComponent是JComponent對象用于繪制自身的三個繪圖方法之一,三個方法的調用順序是:
paintComponent -- 繪圖的主要方法。 缺省時,假如組件不透明它首先繪制背景,然后它執行其它自定義繪圖操作。
paintBorder -- 告訴組件的邊界(假如有的話)進行繪制。 不用調用或者重寫這個方法。
paintChildren -- 告訴這個組件包含的所有組件繪制它們自己。 不用調用或者重寫這個方法。


--------------------------------------------------------------------------------
注重: 不要重寫或者調用調用了paintXxx方法的方法:paint。雖然重寫paint方法在先前的Swing組件中是合法的,但是通常在一個從JComponent派生的組件中這樣做不是一件好事。除非你非常小心,重寫paint 很輕易搞亂繪圖系統,繪圖系統依靠JComponent實現的 paint 方法進行正確的繪圖、性能增強和諸如雙緩沖這樣的特性。
--------------------------------------------------------------------------------

標準的Swing組件將它們的look-and-feel-specific繪制委托給一個稱為UI delegate(UI代理)的對象。當這樣一個組件的paintComponent 方法被調用時,方法請求UI 代理繪制組件。通常,UI代理首先檢查組件是否是不透明的,假如是,繪制組件的整個背景。然后UI代理執行任何look-and-feel-specific繪制。

我們推薦擴展JPanel而不是JComponent的原因是JComponent類目前沒有設置一個UI代理 -- 只有它的子類設置了。這意味著假如你擴展JComponent,你的組件在你自己不繪制的情況下不會被繪制。當你擴展JPanel并且在你的paintComponent方法的開始調用super.paintComponent方法時,那么面板的UI代理在組件不透明的情況下繪制組件的背景。

假如你需要關于繪圖的更多信息,參看 AWT和Swing中的繪圖。它是 Swing Connection中的一篇深入討論繪圖的復雜細節的論文。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 抚宁县| 延长县| 永城市| 邻水| 松江区| 渭南市| 得荣县| 大足县| 清流县| 象山县| 莫力| 格尔木市| 镇雄县| 延寿县| 盐津县| 广昌县| 炎陵县| 辽中县| 仁怀市| 博罗县| 关岭| 文化| 廉江市| 宣威市| 徐闻县| 图片| 佛学| 渝中区| 屯门区| 保靖县| 淮安市| 依兰县| 安化县| 中卫市| 苍山县| 曲松县| 高碑店市| 大庆市| 兴化市| 故城县| 游戏|