日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

Java Swing讀書筆記之一 (Java極富客戶端效果開發(fā))

 fenyu8 2011-07-17
1.    main函數(shù)不要通過直接調(diào)用JFrame子類的構造來啟動窗體程序,因為main本身并非運行于EDT中,因此可能會給UI帶來同步問題,建議使用一下方式運行:
 1     public static void main(String args[]) {
2 Runnable doCreateAndShowGUI = new Runnable() {
3 @Override
4 public void run() {
5 //該方法為該類的私有靜態(tài)方法,用于啟動JFrame的主界面。
6 createAndShowGUI();
7 }
8 };
9 SwingUtilities.invokeLater(doCreateAndShowGUI);
10 }

 

2.    基于重載JComponent的paint屬性來重繪該組件的所有區(qū)域,paint中的Graphics參數(shù)是自始至終存在并保持一致的,paintComponent中的Graphics參數(shù)則是在swing框架每次調(diào)用paintComponent函數(shù)之前新創(chuàng)建的。

 1     public void paint(Graphics g) {
2 // Create an image for the button graphics if necessary
3 if (buttonImage == null || buttonImage.getWidth() != getWidth() ||
4 buttonImage.getHeight() != getHeight()) {
5 //該函數(shù)來自Component,用于獲取當前顯示設備的metrics信息,
6         //然后根據(jù)該信息在創(chuàng)建設備兼容的BufferedImage對象。
7 buttonImage = getGraphicsConfiguration().createCompatibleImage(getWidth(), getHeight());
8 }
9 Graphics gButton = buttonImage.getGraphics();
10 gButton.setClip(g.getClip());
11
12 //使用超類中的paint方法將需要顯示的image繪制到該內(nèi)存圖像中,不推薦直接從superclass繪制到g
13 //(子類paint方法的graphics對象),因為這樣做可能會將superclass的graphics中的設備覆蓋掉子
14 //類graphics中的設置,這樣通過間接的內(nèi)存圖像可以避免該問題。
15 super.paint(gButton);
16
17 //這里必須直接目的graphics的composite屬性,如果只是修改內(nèi)存圖像的composite,將只會是組件的
18 //內(nèi)存被渲染成指定的alpha值,但是如果是設置的目的graphics的composite,那么整個組件內(nèi)存圖像的
19 //顯示將被渲染成指定的透明度。
20 Graphics2D g2d = (Graphics2D)g;
21 AlphaComposite newComposite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .5f);
22 g2d.setComposite(newComposite);
23
24 // Copy the button's image to the destination graphics, translucently
25 g2d.drawImage(buttonImage, 0, 0, null);
26 }

 

3.    不要在EDT(Event Dispatch Thread)中執(zhí)行較長的操作,從而避免UI被凍結的現(xiàn)象發(fā)生。
       和操作UI相關的code務必要放到EDT中調(diào)用,否則容易導致死鎖。
       SwingUtilities.invodeLater(new Runnable()) 可以在EDT之外執(zhí)行該方法,必將和UI操作相關的coding放到參數(shù)Runnable的實現(xiàn)中。該工具函數(shù)會自動將Runnable投遞到EDT中執(zhí)行。
       SwingUtilities.isEventDispatchThread() 如果當前的執(zhí)行線程為EDT,該方法返回true,因此可以直接操作UI,否則表示EDT之外的線程,操作UI的界面不能直接在這里調(diào)用了。

 1     private void incrementLabel() {
2 tickCounter++;
3 Runnable code = new Runnable() {
4 public void run() {
5 counter.setText(String.valueOf(tickCounter));
6 }
7 }
8
9 if (SwingUtilities.isEventDispatchThread())
10 code.run()
11 else
12 SwingUtilities.invokeLater(code);
13 }

       SwingUtilities.invokeAndWait(), 該函數(shù)和invokeLater的主要區(qū)別就是該函數(shù)執(zhí)行后將等待EDT線程執(zhí)行該任務的完成,之后該函數(shù)才正常返回。
       非EDT線程可以通過調(diào)用repaint,強制EDT執(zhí)行組件重繪。


4.    java.util.Timer 定時器中的TimerTask是在EDT之外的獨立線程中執(zhí)行的,因為不能直接執(zhí)行UI的操作。
    java.swing.Timer 定時器中的任務是在EDT中執(zhí)行的,因為可以包含直接操作UI的代碼。
       
5.    打開抗鋸齒:     

       g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
       以下的例子是根據(jù)OS桌面的設置,為文本的渲染打開抗鋸齒。

 1     protected void paintComponent(Graphics g) {
2 Graphics2D g2d = (Graphics2D)g;
3 g2d.setColor(Color.WHITE);
4 g2d.fillRect(0, 0, getWidth(), getHeight());
5 g2d.setColor(Color.BLACK);
6 //該文本的渲染為非抗鋸齒。
7 g2d.drawString("Unhinted string", 10, 20);
8
9 Toolkit tk = Toolkit.getDefaultToolkit();
10 desktopHints = (Map)(tk.getDesktopProperty("awt.font.desktophints"));
11 if (desktopHints != null) {
12 g2d.addRenderingHints(desktopHints);
13 }
14 g2d.drawString("Desktop-hinted string", 10, 40);
15 }

 

6.    圖像縮放的提示:
       RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR //速度最快,效果最差
       RenderingHints.VALUE_INTERPOLATION_BILINEAR    //速度和效果適中
       RenderingHints.VALUE_INTERPOLATION_BICUBIC  //速度最慢,效果最好
       
7.    通過copyArea可以獲取更好的性能
       copyArea(int x,int y,int width,int height,int dx,int dy);
       其中,x和y是需要被復制區(qū)域的左上角坐標,width和height分別表示該區(qū)域的寬度和高度,dx和dy表示相對于此區(qū)域的位置,如果為正值,則表示此區(qū)域的右邊和下邊,如果為負值,則表示此區(qū)域的左邊和上邊。

8.    通過逐次迭代的方式可以獲得更好的效果,同時性能也非常不錯,見下例:

 1     public BufferedImage getFasterScaledInstance(BufferedImage img,
2 int targetWidth, int targetHeight, Object hint,
3 boolean progressiveBilinear)
4 {
5 int type = (img.getTransparency() == Transparency.OPAQUE) ?
6 BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
7 BufferedImage ret = img;
8 BufferedImage scratchImage = null;
9 Graphics2D g2 = null;
10 int w, h;
11 int prevW = ret.getWidth();
12 int prevH = ret.getHeight();
13 boolean isTranslucent = img.getTransparency() != Transparency.OPAQUE;
14
15 if (progressiveBilinear) {
16 // Use multi-step technique: start with original size, then
17 // scale down in multiple passes with drawImage()
18 // until the target size is reached
19 w = img.getWidth();
20 h = img.getHeight();
21 } else {
22 // Use one-step technique: scale directly from original
23 // size to target size with a single drawImage() call
24 w = targetWidth;
25 h = targetHeight;
26 }
27
28 do {
29 if (progressiveBilinear && w > targetWidth) {
30 w /= 2;
31 if (w < targetWidth)
32 w = targetWidth;
33 }
34
35 if (progressiveBilinear && h > targetHeight) {
36 h /= 2;
37 if (h < targetHeight)
38 h = targetHeight;
39 }
40
41 if (scratchImage == null || isTranslucent) {
42 // Use a single scratch buffer for all iterations
43 // and then copy to the final, correctly-sized image
44 // before returning
45 scratchImage = new BufferedImage(w, h, type);
46 g2 = scratchImage.createGraphics();
47 }
48 g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
49 g2.drawImage(ret, 0, 0, w, h, 0, 0, prevW, prevH, null);
50 prevW = w;
51 prevH = h;
52
53 ret = scratchImage;
54 } while (w != targetWidth || h != targetHeight);
55
56 if (g2 != null)
57 g2.dispose();
58
59 // If we used a scratch buffer that is larger than our target size,
60 // create an image of the right size and copy the results into it
61 if (targetWidth != ret.getWidth() || targetHeight != ret.getHeight()) {
62 scratchImage = new BufferedImage(targetWidth, targetHeight, type);
63 g2 = scratchImage.createGraphics();
64 g2.drawImage(ret, 0, 0, null);
65 g2.dispose();
66 ret = scratchImage;
67 }
68 return ret;
69 }

 

9.    將一個普通的圖像復制到一個顯示設備兼容的圖像中,以提高后期渲染操作的性能。
       注:由于顯示設備兼容圖像的圖像數(shù)據(jù)存儲方式和設備顯示時的數(shù)據(jù)讀取方式一致,不需要額外的轉(zhuǎn)換,因此只是需要簡單且高效的內(nèi)存copy即可。

1     void drawCompatibleImage(BufferedImage suboptimalImage) {
2 GraphicsConfiguration gc = getConfiguration();
3 BufferedImage compatibleImage = gc.createCompatibleImage(
4 suboptimalImage.getWidth(),suboptimalImage.getHeight());
5 Graphics g = compatibleImage.getGraphics();
6 g.drawImage(suboptimalImage,0,0,null);
7 }

       制作一個自己的工具類便于創(chuàng)建設備兼容的圖像。

 1     public class MineCompatible {
2 public static GraphicsConfiguration getConfiguration() {
3 return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
4 }
5
6 public static BufferedImage createCompatibleImage(BufferedImage image) {
7 return createCompatibleImage(image,image.getWidth(),image.getHeight());
8 }
9
10 public static BufferedImage createCompatibleImage(BufferedImage image,int width,int height) {
11 return getConfiguration().createCompatibleImage(width,height,image.getTransparency());
12 }
13
14 public static BufferedImage createCompatibleImage(int width,int height) {
15 return getConfiguration().createCompatibleImage(width,height);
16 }
17
18 public static BufferedImage createCompatibleTranslucentImage(int width,int height) {
19 return getConfiguration().createCompatibleImage(width,height,Transparency.TRANSLUCENT);
20 }
21
22 public static BufferedImage loadCompatibleImage(URL resource) throws IOException {
23 BufferedImage image = ImageIO.read(resource);
24 return toCompatibleImage(image);
25 }
26
27 public static BufferedImage toCompatibleImage(BufferedImage image) {
28 GraphicsConfiguration gc = getConfiguration();
29 if (image.getColorModel().equals(gc.getColorModel())
30 return image;
31
32 BufferedImage compatibleImage = gc.createCompatibleImage(
33 image.getWidth(),image.getHeight(),image.getTransparency());
34 Graphics g = compatibleImage.getGraphics();
35 g.drawImage(image,0,0,null);
36 g.dispose();
37 return compatibleImage;
38 }
39 }

 

10.  托管圖像:非托管的圖像在設備顯示時,是從system memory通過總線copy到VRAM的,托管圖像將會在VRAM中創(chuàng)建一個system memory圖像的副本,需要設備顯示時,直接將VRAM中的副本copy到VRAM中用于顯示,從而避免了通過總線將數(shù)據(jù)從system memory拷貝到VRAM了。
      抑制圖像自動托管的兩個因素:
      1) 通過Image的Raster,調(diào)用dataBuffer方法直接獲取并且操作顯示數(shù)據(jù)時,java 2d將自動關閉圖像托管,由于此時是外部代碼直接以數(shù)組的方式操作顯示圖像數(shù)據(jù),因此java 2d無法監(jiān)控顯示數(shù)據(jù)是否已經(jīng)被改變。注:一旦獲取dataBuffer后,無法在通過將dataBuffer交還的方法重新使該圖像成為托管圖像。
      DataBuffer dataBuffer = image.getRaster().getDataBuffer();
      2) 頻繁的渲染到圖像,和1)不同,java 2d可以根據(jù)實際情況動態(tài)的處理是否需要將該圖像設置為托管圖像。
      由于渲染操作頻繁,致使從system memory到與VRAM中的副本進行同步的操作變?yōu)轭~外的操作。

11. 通過保存中間圖像的方式盡可能減少實時渲染的操作,在paintComponent中,通過將中間圖像copy到swing后臺緩沖,這樣可以極大的提高顯示效率,見下例:

 1     private void drawScaled(Graphics g) {
2 long startTime, endTime, totalTime;
3
4 // Scaled image
5 ((Graphics2D)g).setRenderingHint(RenderingHints.KEY_INTERPOLATION,
6 RenderingHints.VALUE_INTERPOLATION_BILINEAR);
7 startTime = System.nanoTime();
8 for (int i = 0; i < 100; ++i) {
9 g.drawImage(picture, SCALE_X, DIRECT_Y, scaleW, scaleH, null);
10 }
11 endTime = System.nanoTime();
12 totalTime = (endTime - startTime) / 1000000;
13 g.setColor(Color.BLACK);
14 g.drawString("Direct: " + ((float)totalTime/100) + " ms",
15 SCALE_X, DIRECT_Y + scaleH + 20);
16 System.out.println("scaled: " + totalTime);
17
18 // Intermediate Scaled
19 // First, create the intermediate image
20 if (scaledImage == null ||
21 scaledImage.getWidth() != scaleW ||
22 scaledImage.getHeight() != scaleH)
23 {
24 GraphicsConfiguration gc = getGraphicsConfiguration();
25 scaledImage = gc.createCompatibleImage(scaleW, scaleH);
26 Graphics gImg = scaledImage.getGraphics();
27 ((Graphics2D)gImg).setRenderingHint(RenderingHints.KEY_INTERPOLATION,
28 RenderingHints.VALUE_INTERPOLATION_BILINEAR);
29 gImg.drawImage(picture, 0, 0, scaleW, scaleH, null);
30 }
31 // Now, copy the intermediate image into place
32 startTime = System.nanoTime();
33 for (int i = 0; i < 100; ++i) {
34 g.drawImage(scaledImage, SCALE_X, INTERMEDIATE_Y, null);
35 }
36 endTime = System.nanoTime();
37 totalTime = (endTime - startTime) / 1000000;
38 g.drawString("Intermediate: " + ((float)totalTime/100) + " ms",
39 SCALE_X, INTERMEDIATE_Y + scaleH + 20);
40 System.out.println("Intermediate scaled: " + totalTime);
41 }

       需要實時渲染和計算的不規(guī)則圖形也可以很好的利用中間圖像,以避免更多的計算,但是對于不規(guī)則圖像,需要考慮將中間圖像的底色設置為透明,以便在copy的過程中只是復制這個圖形的顏色,而不會復制這個圖像的背景色,見下例(其中BITMASK為設置透明背景):

 1     private void renderSmiley(Graphics g, int x, int y) {
2 Graphics2D g2d = (Graphics2D)g.create();
3
4 // Yellow face
5 g2d.setColor(Color.yellow);
6 g2d.fillOval(x, y, SMILEY_SIZE, SMILEY_SIZE);
7
8 // Black eyes
9 g2d.setColor(Color.black);
10 g2d.fillOval(x + 30, y + 30, 8, 8);
11 g2d.fillOval(x + 62, y + 30, 8, 8);
12
13 // Black outline
14 g2d.drawOval(x, y, SMILEY_SIZE, SMILEY_SIZE);
15
16 // Black smile
17 g2d.setStroke(new BasicStroke(3.0f));
18 g2d.drawArc(x + 20, y + 20, 60, 60, 190, 160);
19
20 g2d.dispose();
21 }
22
23 /**
24 * Draws both the direct and intermediate-image versions of a
25 * smiley face, timing both variations.
26 */
27 private void drawSmiley(Graphics g) {
28 long startTime, endTime, totalTime;
29
30 // Draw smiley directly
31 startTime = System.nanoTime();
32 for (int i = 0; i < 100; ++i) {
33 renderSmiley(g, SMILEY_X, DIRECT_Y);
34 }
35 endTime = System.nanoTime();
36 totalTime = (endTime - startTime) / 1000000;
37 g.setColor(Color.BLACK);
38 g.drawString("Direct: " + ((float)totalTime/100) + " ms",
39 SMILEY_X, DIRECT_Y + SMILEY_SIZE + 20);
40 System.out.println("Direct: " + totalTime);
41
42 // Intermediate Smiley
43 // First, create the intermediate image if necessary
44 if (smileyImage == null) {
45 GraphicsConfiguration gc = getGraphicsConfiguration();
46 smileyImage = gc.createCompatibleImage(
47 SMILEY_SIZE + 1, SMILEY_SIZE + 1, Transparency.BITMASK);
48 Graphics2D gImg = (Graphics2D)smileyImage.getGraphics();
49 renderSmiley(gImg, 0, 0);
50 gImg.dispose();
51 }
52 // Now, copy the intermediate image
53 startTime = System.nanoTime();
54 for (int i = 0; i < 100; ++i) {
55 g.drawImage(smileyImage, SMILEY_X, INTERMEDIATE_Y, null);
56 }
57 endTime = System.nanoTime();
58 totalTime = (endTime - startTime) / 1000000;
59 g.drawString("Intermediate: " + ((float)totalTime/100) + " ms",
60 SMILEY_X, INTERMEDIATE_Y + SMILEY_SIZE + 20);
61 System.out.println("intermediate smiley: " + totalTime);
62 }

       如果中間圖像使用半透明效果,不像以上兩個例子分為使用了不透明和全透明背景,硬件加速將會被抑制。

 

1.    main函數(shù)不要通過直接調(diào)用JFrame子類的構造來啟動窗體程序,因為main本身并非運行于EDT中,因此可能會給UI帶來同步問題,建議使用一下方式運行:

    public static void main(String args[]) {
        Runnable doCreateAndShowGUI = new Runnable() {
            @Override
            public void run() {
                createAndShowGUI(); //該方法為該類的私有靜態(tài)方法,用于啟動JFrame的主界面。
            }
        };
        SwingUtilities.invokeLater(doCreateAndShowGUI);
    }
    
2.    基于重載JComponent的paint屬性來重繪該組件的所有區(qū)域,paint中的Graphics參數(shù)是自始至終存在并保持一致的,paintComponent中的Graphics參數(shù)則是在swing框架每次調(diào)用paintComponent函數(shù)之前新創(chuàng)建的。
    public void paint(Graphics g) {
        // Create an image for the button graphics if necessary
        if (buttonImage == null || buttonImage.getWidth() != getWidth() ||
            buttonImage.getHeight() != getHeight()) {
            //該函數(shù)來自Component,用于獲取當前顯示設備的metrics信息,然后根據(jù)該信息在創(chuàng)建設備兼容的BufferedImage對象。
            buttonImage = getGraphicsConfiguration().createCompatibleImage(getWidth(), getHeight());
        }
        Graphics gButton = buttonImage.getGraphics();
        gButton.setClip(g.getClip());
        
        //使用超類中的paint方法將需要顯示的image繪制到該內(nèi)存圖像中,不推薦直接從superclass繪制到g(子類paint方法的graphics對象),因為這樣做可能會將superclass的graphics中的設備覆蓋掉子類graphics中的設置,這樣通過間接的內(nèi)存圖像可以避免該問題。
        super.paint(gButton);
        
        //這里必須直接目的graphics的composite屬性,如果只是修改內(nèi)存圖像的composite,將只會是組件的內(nèi)存被渲染成指定的alpha值,但是如果是設置的目的graphics的composite,那么整個組件內(nèi)存圖像的顯示將被渲染成指定的透明度。
        Graphics2D g2d  = (Graphics2D)g;
        AlphaComposite newComposite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .5f);
        g2d.setComposite(newComposite);
        
        // Copy the button's image to the destination graphics, translucently
        g2d.drawImage(buttonImage, 0, 0, null);
    }

3.    不要在EDT(Event Dispatch Thread)中執(zhí)行較長的操作,從而避免UI被凍結的現(xiàn)象發(fā)生。
    和操作UI相關的code務必要放到EDT中調(diào)用,否則容易導致死鎖。
    SwingUtilities.invodeLater(new Runnable()) 可以在EDT之外執(zhí)行該方法,必將和UI操作相關的coding放到參數(shù)Runnable的實現(xiàn)中。該工具函數(shù)會自動將Runnable投遞到EDT中執(zhí)行。
    SwingUtilities.isEventDispatchThread() 如果當前的執(zhí)行線程為EDT,該方法返回true,因此可以直接操作UI,否則表示EDT之外的線程,操作UI的界面不能直接在這里調(diào)用了。
    private void incrementLabel() {
        tickCounter++;
        Runnable code = new Runnable() {
            public void run() {
                counter.setText(String.valueOf(tickCounter));
            }
        }
        
        if (SwingUtilities.isEventDispatchThread())
            code.run()
        else
            SwingUtilities.invokeLater(code);
    }
    SwingUtilities.invokeAndWait(), 該函數(shù)和invokeLater的主要區(qū)別就是該函數(shù)執(zhí)行后將等待EDT線程執(zhí)行該任務的完成,之后該函數(shù)才正常返回。
    非EDT線程可以通過調(diào)用repaint,強制EDT執(zhí)行組件重繪。
 
4.    java.util.Timer 定時器中的TimerTask是在EDT之外的獨立線程中執(zhí)行的,因為不能直接執(zhí)行UI的操作。
    java.swing.Timer 定時器中的任務是在EDT中執(zhí)行的,因為可以包含直接操作UI的代碼。
        
5.    打開抗鋸齒:g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
    以下的例子是根據(jù)OS桌面的設置,為文本的渲染打開抗鋸齒。
        
    protected void paintComponent(Graphics g) {
        Graphics2D g2d = (Graphics2D)g;
        g2d.setColor(Color.WHITE);
        g2d.fillRect(0, 0, getWidth(), getHeight());
        g2d.setColor(Color.BLACK);
                //該文本的渲染為非抗鋸齒。        
        g2d.drawString("Unhinted string", 10, 20);

        Toolkit tk = Toolkit.getDefaultToolkit();
        desktopHints = (Map)(tk.getDesktopProperty("awt.font.desktophints"));
        if (desktopHints != null) {
            g2d.addRenderingHints(desktopHints);
        }
        g2d.drawString("Desktop-hinted string", 10, 40);
    }
    
6.    圖像縮放的提示:
    RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR //速度最快,效果最差
    RenderingHints.VALUE_INTERPOLATION_BILINEAR    //速度和效果適中
    RenderingHints.VALUE_INTERPOLATION_BICUBIC  //速度最慢,效果最好
        
7.    通過copyArea可以獲取更好的性能
    copyArea(int x,int y,int width,int height,int dx,int dy);
    其中,x和y是需要被復制區(qū)域的左上角坐標,width和height分別表示該區(qū)域的寬度和高度,dx和dy表示相對于此區(qū)域的位置,如果為正值,則表示此區(qū)域的右邊和下邊,如果為負值,則表示此區(qū)域的左邊和上邊。

8.    通過逐次迭代的方式可以獲得更好的效果,同時性能也非常不錯,見下例:
    public BufferedImage getFasterScaledInstance(BufferedImage img,
            int targetWidth, int targetHeight, Object hint,
            boolean progressiveBilinear)
    {
        int type = (img.getTransparency() == Transparency.OPAQUE) ?
            BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
        BufferedImage ret = img;
        BufferedImage scratchImage = null;
        Graphics2D g2 = null;
        int w, h;
        int prevW = ret.getWidth();
        int prevH = ret.getHeight();
        boolean isTranslucent = img.getTransparency() !=  Transparency.OPAQUE;

        if (progressiveBilinear) {
            // Use multi-step technique: start with original size, then
            // scale down in multiple passes with drawImage()
            // until the target size is reached
            w = img.getWidth();
            h = img.getHeight();
        } else {
            // Use one-step technique: scale directly from original
            // size to target size with a single drawImage() call
            w = targetWidth;
            h = targetHeight;
        }
        
        do {
            if (progressiveBilinear && w > targetWidth) {
                w /= 2;
                if (w < targetWidth)
                    w = targetWidth;
            }

            if (progressiveBilinear && h > targetHeight) {
                h /= 2;
                if (h < targetHeight)
                    h = targetHeight;
            }

            if (scratchImage == null || isTranslucent) {
                // Use a single scratch buffer for all iterations
                // and then copy to the final, correctly-sized image
                // before returning
                scratchImage = new BufferedImage(w, h, type);
                g2 = scratchImage.createGraphics();
            }
            g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
            g2.drawImage(ret, 0, 0, w, h, 0, 0, prevW, prevH, null);
            prevW = w;
            prevH = h;

            ret = scratchImage;
        } while (w != targetWidth || h != targetHeight);
        
        if (g2 != null)
            g2.dispose();

        // If we used a scratch buffer that is larger than our target size,
        // create an image of the right size and copy the results into it
        if (targetWidth != ret.getWidth() || targetHeight != ret.getHeight()) {
            scratchImage = new BufferedImage(targetWidth, targetHeight, type);
            g2 = scratchImage.createGraphics();
            g2.drawImage(ret, 0, 0, null);
            g2.dispose();
            ret = scratchImage;
        }      
        return ret;
    }
    
9.    將一個普通的圖像復制到一個顯示設備兼容的圖像中,以提高后期渲染操作的性能。
    注:由于顯示設備兼容圖像的圖像數(shù)據(jù)存儲方式和設備顯示時的數(shù)據(jù)讀取方式一致,不需要額外的轉(zhuǎn)換,因此只是需要簡單且高效的內(nèi)存copy即可。
    void drawCompatibleImage(BufferedImage suboptimalImage) {
        GraphicsConfiguration gc = getConfiguration();
        BufferedImage compatibleImage = gc.createCompatibleImage(
                suboptimalImage.getWidth(),suboptimalImage.getHeight());
        Graphics g = compatibleImage.getGraphics();
        g.drawImage(suboptimalImage,0,0,null);
    }
    
    制作一個自己的工具類便于創(chuàng)建設備兼容的圖像。
    public class MineCompatible {
        public static GraphicsConfiguration getConfiguration() {
            return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
        }

        public static BufferedImage createCompatibleImage(BufferedImage image) {
            return createCompatibleImage(image,image.getWidth(),image.getHeight());
        }
        
        public static BufferedImage createCompatibleImage(BufferedImage image,int width,int height) {
            return getConfiguration().createCompatibleImage(width,height,image.getTransparency());
        }
        
        public static BufferedImage createCompatibleImage(int width,int height) {
            return getConfiguration().createCompatibleImage(width,height);
        }
        
        public static BufferedImage createCompatibleTranslucentImage(int width,int height) {
            return getConfiguration().createCompatibleImage(width,height,Transparency.TRANSLUCENT);
        }
        
        public static BufferedImage loadCompatibleImage(URL resource) throws IOException {
            BufferedImage image = ImageIO.read(resource);
            return toCompatibleImage(image);
        }
        
        public static BufferedImage toCompatibleImage(BufferedImage image) {
            GraphicsConfiguration gc = getConfiguration();
            if (image.getColorModel().equals(gc.getColorModel())
                return image;
                    
            BufferedImage compatibleImage = gc.createCompatibleImage(
                    image.getWidth(),image.getHeight(),image.getTransparency());
            Graphics g = compatibleImage.getGraphics();
            g.drawImage(image,0,0,null);
            g.dispose();
            return compatibleImage;
        }
    }
        
10.    托管圖像:非托管的圖像在設備顯示時,是從system memory通過總線copy到VRAM的,托管圖像將會在VRAM中創(chuàng)建一個system memory圖像的副本,需要設備顯示時,直接將VRAM中的副本copy到VRAM中用于顯示,從而避免了通過總線將數(shù)據(jù)從system memory拷貝到VRAM了。
    抑制圖像自動托管的兩個因素:
    1) 通過Image的Raster,調(diào)用dataBuffer方法直接獲取并且操作顯示數(shù)據(jù)時,java 2d將自動關閉圖像托管,由于此時是外部代碼直接以數(shù)組的方式操作顯示圖像數(shù)據(jù),因此java 2d無法監(jiān)控顯示數(shù)據(jù)是否已經(jīng)被改變。注:一旦獲取dataBuffer后,無法在通過將dataBuffer交還的方法重新使該圖像成為托管圖像。
    DataBuffer dataBuffer = image.getRaster().getDataBuffer();
    2) 頻繁的渲染到圖像,和1)不同,java 2d可以根據(jù)實際情況動態(tài)的處理是否需要將該圖像設置為托管圖像。
    由于渲染操作頻繁,致使從system memory到與VRAM中的副本進行同步的操作變?yōu)轭~外的操作。

11.    通過保存中間圖像的方式盡可能減少實時渲染的操作,在paintComponent中,通過將中間圖像copy到swing后臺緩沖,這樣可以極大的提高顯示效率,見下例:
    private void drawScaled(Graphics g) {
        long startTime, endTime, totalTime;
        
        // Scaled image
        ((Graphics2D)g).setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        startTime = System.nanoTime();
        for (int i = 0; i < 100; ++i) {
            g.drawImage(picture, SCALE_X, DIRECT_Y, scaleW, scaleH, null);
        }
        endTime = System.nanoTime();
        totalTime = (endTime - startTime) / 1000000;
        g.setColor(Color.BLACK);
        g.drawString("Direct: " + ((float)totalTime/100) + " ms",
                SCALE_X, DIRECT_Y + scaleH + 20);
        System.out.println("scaled: " + totalTime);
        
        // Intermediate Scaled
        // First, create the intermediate image
        if (scaledImage == null ||
            scaledImage.getWidth() != scaleW ||
            scaledImage.getHeight() != scaleH)
        {
            GraphicsConfiguration gc = getGraphicsConfiguration();
            scaledImage = gc.createCompatibleImage(scaleW, scaleH);
            Graphics gImg = scaledImage.getGraphics();
            ((Graphics2D)gImg).setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                    RenderingHints.VALUE_INTERPOLATION_BILINEAR);
            gImg.drawImage(picture, 0, 0, scaleW, scaleH, null);
        }
        // Now, copy the intermediate image into place
        startTime = System.nanoTime();
        for (int i = 0; i < 100; ++i) {
            g.drawImage(scaledImage, SCALE_X, INTERMEDIATE_Y, null);
        }
        endTime = System.nanoTime();
        totalTime = (endTime - startTime) / 1000000;
        g.drawString("Intermediate: " + ((float)totalTime/100) + " ms",
                SCALE_X, INTERMEDIATE_Y + scaleH + 20);
        System.out.println("Intermediate scaled: " + totalTime);
    }
    需要實時渲染和計算的不規(guī)則圖形也可以很好的利用中間圖像,以避免更多的計算,但是對于不規(guī)則圖像,需要考慮將中間圖像的底色設置為透明,以便在copy的過程中只是復制這個圖形的顏色,而不會復制這個圖像的背景色,見下例(其中BITMASK為設置透明背景):
    private void renderSmiley(Graphics g, int x, int y) {
        Graphics2D g2d = (Graphics2D)g.create();
            
        // Yellow face
        g2d.setColor(Color.yellow);
        g2d.fillOval(x, y, SMILEY_SIZE, SMILEY_SIZE);
            
        // Black eyes
        g2d.setColor(Color.black);
        g2d.fillOval(x + 30, y + 30, 8, 8);
        g2d.fillOval(x + 62, y + 30, 8, 8);
            
        // Black outline
        g2d.drawOval(x, y, SMILEY_SIZE, SMILEY_SIZE);
            
        // Black smile
        g2d.setStroke(new BasicStroke(3.0f));
        g2d.drawArc(x + 20, y + 20, 60, 60, 190, 160);
        
        g2d.dispose();
    }
    
    /**
     * Draws both the direct and intermediate-image versions of a
     * smiley face, timing both variations.
     */
    private void drawSmiley(Graphics g) {
        long startTime, endTime, totalTime;

        // Draw smiley directly
        startTime = System.nanoTime();
        for (int i = 0; i < 100; ++i) {
            renderSmiley(g, SMILEY_X, DIRECT_Y);
        }
        endTime = System.nanoTime();
        totalTime = (endTime - startTime) / 1000000;
        g.setColor(Color.BLACK);
        g.drawString("Direct: " + ((float)totalTime/100) + " ms",
                SMILEY_X, DIRECT_Y + SMILEY_SIZE + 20);
        System.out.println("Direct: " + totalTime);
        
        // Intermediate Smiley
        // First, create the intermediate image if necessary
        if (smileyImage == null) {
            GraphicsConfiguration gc = getGraphicsConfiguration();
            smileyImage = gc.createCompatibleImage(
                          SMILEY_SIZE + 1, SMILEY_SIZE + 1, Transparency.BITMASK);
            Graphics2D gImg = (Graphics2D)smileyImage.getGraphics();
            renderSmiley(gImg, 0, 0);
            gImg.dispose();
        }
        // Now, copy the intermediate image
        startTime = System.nanoTime();
        for (int i = 0; i < 100; ++i) {
            g.drawImage(smileyImage, SMILEY_X, INTERMEDIATE_Y, null);
        }
        endTime = System.nanoTime();
        totalTime = (endTime - startTime) / 1000000;
        g.drawString("Intermediate: " + ((float)totalTime/100) + " ms",
                SMILEY_X, INTERMEDIATE_Y + SMILEY_SIZE + 20);
        System.out.println("intermediate smiley: " + totalTime);
    }

    如果中間圖像使用半透明效果,不像以上兩個例子分為使用了不透明和全透明背景,硬件加速將會被抑制。
    
12.    明確的告訴java 2d你將要完成的繪制,而不是使用一個更為通用的方式,這樣能夠帶來更好的性能。
    
    //畫線的bad way
    Shape line = new Line2D.Double(LINE_X, BAD_Y, LINE_X + 50, BAD_Y + 50);
    g2d.draw(line);
    
    //畫線的good way
    g.drawLine(LINE_X, GOOD_Y, LINE_X + 50, GOOD_Y + 50);
    
    //畫rectangle的bad way
    Shape rect = new Rectangle(RECT_X, BAD_Y, 50, 50);
    g2d.fill(rect);
    
    //畫rectangle的good way
    g.fillRect(RECT_X, GOOD_Y, 50, 50);
    
13.    圖像合成,其中最為有用的三個規(guī)則分別是clear、SrcOver(swing缺省)和SrcIn。
    Clear:是擦掉一個圖像的背景以便使他變得完全透明的一個容易的方式,可以將其理解為Photoshop中的橡皮擦,通過Clear可以清除任意形狀的區(qū)域。
    public void exampleForClear() {
        BufferedImage image = new BufferedImage(200,200,BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2 = image.createGraphics();
        //draw something here.
        //...
        //Erase the content of the image.
        g2.setComposite(AlphaComposite.Clear);
        //The color,the Paint, etc. do not matter
        g2.fillRect(0,0,image.getWidth(),image.getHeight());
    }
    SrcOver: 其運算公式為Ar = As + Ad * (1 - As), Cr = Cs + Cd * (1 - As), 其中Ar為結果Alpha,As表示源圖像的Alpha,As為目的圖像的Alpha,Cr表示(RGB)中每個通道的結果值,Cs為源圖像中(RGB)單個通道的值,Cd為目的圖像的單個通道值。
    一般的用法為在目的圖像上繪制半透明的源圖像。
    SrcIn:位于目的地內(nèi)部的那部分源代替目的地,位于目的地之外的那部分源丟棄掉。
    protected void paintComponent(Graphics g) {
        BufferedImage temp = new BufferedImage(getWidth(), getHeight(),
            BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2 = temp.createGraphics();
        
        if (shadow.isSelected()) {
            int x = (getWidth() - image.getWidth()) / 2;
            int y = (getHeight() - image.getHeight()) / 2;
            g2.drawImage(image, x + 4, y + 10, null);

            Composite oldComposite = g2.getComposite();
            g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN, 0.75f));
            g2.setColor(Color.BLACK);
            g2.fillRect(0, 0, getWidth(), getHeight());
            g2.setComposite(oldComposite);
            g2.drawImage(image, x, y, null);
        } else {
            int x = (getWidth() - image.getWidth()) / 2;
            int y = (getHeight() - image.getHeight()) / 2;
            g2.drawImage(image, x, y, null);

            Composite oldComposite = g2.getComposite();
            g2.setComposite(AlphaComposite.SrcIn);
            x = (getWidth() - landscape.getWidth()) / 2;
            y = (getHeight() - landscape.getHeight()) / 2;
            g2.drawImage(landscape, x, y, null);
            g2.setComposite(oldComposite);
        }
        
        g2.dispose();
        g.drawImage(temp, 0, 0, null);
    }

14.    利用漸變完成的反射效果,主要分為3個步驟完成,見下例:
    private BufferedImage createReflection(BufferedImage image) {
        int height = image.getHeight();
        
        BufferedImage result = new BufferedImage(image.getWidth(), height * 2,
                BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2 = result.createGraphics();
        //1. 想渲染正常物體一樣渲染它。
        g2.drawImage(image, 0, 0, null);
        
        //2. 渲染這個物體上下顛倒的一個副本
        g2.scale(1.0, -1.0);
        g2.drawImage(image, 0, -height - height, null);
        g2.scale(1.0, -1.0);

        // Move to the origin of the clone
        g2.translate(0, height);
        
        //3. 模糊這個副本的一部分以使它淡出,隨著它遠離最初的物體。
        GradientPaint mask;
        //目的顏色RGB無關重要,alpha值必須為0。
        mask = new GradientPaint(0, 0, new Color(1.0f, 1.0f, 1.0f, 0.5f),
                0, height / 2, new Color(1.0f, 1.0f, 1.0f, 0.0f));
        Paint oldPaint = g2.getPaint();
        g2.setPaint(mask);
        // Sets the alpha composite
        g2.setComposite(AlphaComposite.DstIn);        
        //盡量覆蓋全部顛倒圖像,以避免因覆蓋不全而造成的偽影。
        g2.fillRect(0, 0, image.getWidth(), height);
        g2.dispose();
        return result;
    }
    
15.    線性漸變LinearGradientPaint(float startX,float startY,float endX,float endY,float[] fractions,Color[] colors),這里包含兩個數(shù)組參數(shù),其中第一個float類型的數(shù)組包含漸變中使用的每個顏色的位置。每一對位置/顏色被稱為一個停頓,見下例:
    protected void paintComponent(Graphics g) {
        Graphics2D g2 = (Graphics2D) g;
        Paint oldPaint = g2.getPaint();
        LinearGradientPaint p;
            
        p = new LinearGradientPaint(0.0f, 0.0f, 0.0f, 20.0f,
          new float[] { 0.0f, 0.5f, 0.501f, 1.0f },
          new Color[] { new Color(0x63a5f7),
                        new Color(0x3799f4),
                        new Color(0x2d7eeb),
                        new Color(0x30a5f9) });
        g2.setPaint(p);
        g2.fillRect(0, 0, getWidth(), 21);
        g2.setPaint(oldPaint);
        super.paintComponent(g);
    }

16.    優(yōu)化漸變的3個技巧:
    1)    緩存這個漸變:該解決方案是把這個漸變變成一個圖像并僅僅繪制那個圖像,但是缺點是需要消耗更多的內(nèi)存。
    protected void paintComponent(Graphics g) {
        if (gradientImage == null
            || gradientImage.getWidth() != getWidth()
            || gradientImage.getHeight() != getHeight()) {
            gradientImage = new BufferedImage(getWidth(),getHeigth(),BufferedImage.TYPE_INT_RGB);
            Graphics2D g2d = (Graphics2D)gradientImage.getGraphics();
            g2d.setPaint(backgroundGradient);
            g2d.fillRect(0,0,getWidth(),getHeight());
            g2d.dispose()
        }
        g.drawImage(gradientImage,0,0,null);
    }
    
    2)    更巧妙的緩存:當繪制一個垂直或者水平漸變時,每一列或者每一行都是相同的,因此可以只是保留一列或者一行的數(shù)據(jù),然在需要覆蓋漸變時在拉伸該列或者該行。
    protected void paintComponent(Graphics g) {
        if (gradientImage == null || gradientImage.getHeight() != getHeight()) {
            gradientImage = MineCompatible.createCompatibleImage(1,getHeight());
            Graphics2D g2d = (Graphics2D)gradientImage.getGraphics();
            g2d.setPaint(backgroundGradient);
            g2d.fillRect(0,0,1,getHeight());
            g2d.dispose();
        }
        g.drawImage(gradientImage,0,0,getWidth(),getHeigth(),null);
    }
    
    3)    使用循環(huán)漸變的優(yōu)化:如果漸變只是被覆蓋組件高度的一半時,如以下代碼:
    protected void paintComponent(Graphics g) {
        Graphics2D g2d = (Graphics2D)g.createGraphics();
        g2d.setPaint(new GradientPaint(0.0f,0.0f,Color.WHITE,0.0f,getHeigth()/2.0f,Color.DARK_GRAY);
        g2d.fillRect(0,0,getWidth(),getHeight());
    }
    該代碼將會從組件的(0,0)到(0,height/2)繪制漸變,同時利用這個漸變的最后顏色填充剩下的像素,為了做到這一點,java 2d將不斷的檢查是否當前的像素位于這個漸變區(qū)域的外面,因此對于成千上萬的像素來說,將會花費很多時間。如果使用循環(huán)漸變的方式,java 2d內(nèi)部在渲染的時候?qū)贿M行該判斷,從而大大提高了整體的效率,見如下代碼:
    //循環(huán)GradientPaint
    new GradientPaint(new Point(0,0),Color.WHITE,new Point(0,getHeight()),Color.DARK_GRAY,true/*該標志表示循環(huán)*/);
    //循環(huán)LinearGradientPaint
    new LinearGradientPaint(new Point(0,0),new Point(0,getHeigth()),new float[] {0.0f,1.0f},new Color[] {Color.WHITE,Color.DARK_GRAY},MultipleGradientPaint.CycleMethod.REPEAT);    

17.    圖像處理:
    1)    AffineTransformOp
    public BufferedImage makeeAffineTransformOp(BufferedImage srcImage) {
        //高度和寬度均為源圖像的50%。
        AffineTransform transform = AffineTransform.getScaleInstance(0.5, 0.5);
        AffineTransformOp op = new AffineTransformOp(transform,AffineTransformOp.TYPE_BILINEAR);
        return op.filter(srcImage,null);
    }
    
    2)    RescaleOp
    private BufferedImage makeRescaleOp(BufferedImage srcImage) {
        BufferedImage dstImage = null;
        float[] factors = new float[] { 1.4f, 1.4f, 1.4f };
        float[] offsets = new float[] { 0.0f, 0.0f, 30.0f };
        //RGB每個顏色通道的亮度增加40%,B通道增加30/256=12%的顏色分量。
        RescaleOp op = new RescaleOp(factors, offsets, null);
        return op.filter(srcImage,null);
    }

18.    玻璃窗格的基本繪制技巧:
    1).    給當前JFrame安裝玻璃窗格,安裝后該玻璃窗格的缺省顯示方式是隱藏顯示,即setVisible(false),如果之前JFrame已經(jīng)使用了玻璃窗格,本次操作只是替換一個新的對象,那么該窗格的visible屬性將和原有窗格的visible屬性保持一致。
    public ApplicationFrame() { //ApplicationFrame為應用程序的主窗體,繼承自JFrame
        initComponents();
        //安裝玻璃窗格,glassPane是JComponent的子類。
        setGlassPane(glassPane = new ProgressGlassPane());
    }
    
    2). 實現(xiàn)玻璃窗格的paintComponent方法
    protected void paintComponent(Graphics g) {
        // enables anti-aliasing
        Graphics2D g2 = (Graphics2D) g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        
        // gets the current clipping area
        Rectangle clip = g.getClipBounds();
        
        // sets a 65% translucent composite
        AlphaComposite alpha = AlphaComposite.SrcOver.derive(0.65f);
        Composite composite = g2.getComposite();
        g2.setComposite(alpha);
        
        // fills the background
        g2.setColor(getBackground());
        g2.fillRect(clip.x, clip.y, clip.width, clip.height);
        // centers the progress bar on screen
        FontMetrics metrics = g.getFontMetrics();        
        int x = (getWidth() - BAR_WIDTH) / 2;
        int y = (getHeight() - BAR_HEIGHT - metrics.getDescent()) / 2;
        
        // draws the text
        g2.setColor(TEXT_COLOR);
        g2.drawString(message, x, y);
        // goes to the position of the progress bar
        y += metrics.getDescent();        
        // computes the size of the progress indicator
        int w = (int) (BAR_WIDTH * ((float) progress / 100.0f));
        int h = BAR_HEIGHT;
        
        // draws the content of the progress bar
        Paint paint = g2.getPaint();
        
        // bar's background
        Paint gradient = new GradientPaint(x, y, GRADIENT_COLOR1, x, y + h, GRADIENT_COLOR2);
        g2.setPaint(gradient);
        g2.fillRect(x, y, BAR_WIDTH, BAR_HEIGHT);
        
        // actual progress
        gradient = new LinearGradientPaint(x, y, x, y + h,GRADIENT_FRACTIONS, GRADIENT_COLORS);
        g2.setPaint(gradient);
        g2.fillRect(x, y, w, h);
        g2.setPaint(paint);
        
        // draws the progress bar border
        g2.drawRect(x, y, BAR_WIDTH, BAR_HEIGHT);
        g2.setComposite(composite);
    }
    
    3).    主窗體中的工作線程需要調(diào)用的方法,以便更新進度條的顯示狀態(tài)
    public void setProgress(int progress) {
        int oldProgress = this.progress;
        this.progress = progress;
        
        // computes the damaged area
        FontMetrics metrics = getGraphics().getFontMetrics(getFont());
        int w = (int) (BAR_WIDTH * ((float) oldProgress / 100.0f));
        int x = w + (getWidth() - BAR_WIDTH) / 2;
        int y = (getHeight() - BAR_HEIGHT) / 2;
        y += metrics.getDescent() / 2;
        
        w = (int) (BAR_WIDTH * ((float) progress / 100.0f)) - w;
        int h = BAR_HEIGHT;
        //The reason why uses the following repaint(x, y, w, h) not repaint() is to
        //avoid repainting all the area to improve the performance.
        repaint(x, y, w, h);
    }
    
19.    玻璃窗格中屏蔽輸入事件,上例中繪制的玻璃窗格只是完成了基本的顯示效果,用戶仍然可以操作玻璃窗格覆蓋下的控件,這樣會給用戶帶來非常迷惑的感覺,因此需要屏蔽玻璃窗格覆蓋下的控件獲取來自鼠標和鍵盤的事件。
    1).    為玻璃窗格控件自身添加空的鼠標和鍵盤的監(jiān)聽器
    public ProgressGlassPane() {
        // blocks all user input
        addMouseListener(new MouseAdapter() { });
        addMouseMotionListener(new MouseMotionAdapter() { });
        addKeyListener(new KeyAdapter() { });
    }
    
    2).    以上操作只是較好的屏蔽了鼠標事件,但是對于鍵盤事件,由于swing將鍵盤事件直接發(fā)送到當前聚焦的控件,因此如果有一組控件已經(jīng)獲取了焦點,它仍然可以收到鍵盤按鍵事件,甚至可以通過tab或ctrl+tab在各個控件之間切換焦點。要完成該功能,需要在玻璃窗體變成可見時調(diào)用requestFocusInWindow()以奪取焦點,因此該段代碼仍然需要放在該對象的構造函數(shù)中,如下:
    public ProgressGlassPane() {
        // blocks all user input
        addMouseListener(new MouseAdapter() { });
        addMouseMotionListener(new MouseMotionAdapter() { });
        addKeyListener(new KeyAdapter() { });

        //This event will be triggered when this component turn to be visible.        
        addComponentListener(new ComponentAdapter() {
            public void componentShown(ComponentEvent evt) {
                requestFocusInWindow();
            }
        });
    }
    
    3).    此時用戶仍然可以通過tab鍵將焦點傳入玻璃窗格覆蓋的控件中,因此需要在構造函數(shù)中調(diào)用setFocusTraversalKeysEnabled(false)以便禁用該功能。
    public ProgressGlassPane() {
        // blocks all user input
        addMouseListener(new MouseAdapter() { });
        addMouseMotionListener(new MouseMotionAdapter() { });
        addKeyListener(new KeyAdapter() { });

        setFocusTraversalKeysEnabled(false);
        //This event will be triggered when this component turn to be visible.        
        addComponentListener(new ComponentAdapter() {
            public void componentShown(ComponentEvent evt) {
                requestFocusInWindow();
            }
        });
    }
    
20.    屏蔽玻璃窗格中部分區(qū)域的鼠標事件,比如在一個完全透明的窗格中的左下角繪制一個公司的logo,其他部分則完全透明,此時,如果用戶將鼠標放到玻璃窗格下面的控件上方時,由于JFrame的最頂層組件是玻璃窗格,因此他攔截了鼠標光標的顯示效果,比如其下擺放了一組輸入框,如果沒有玻璃窗格,那么當鼠標停留在控件上方時,swing會根據(jù)實際控件的類型更新鼠標光標的形狀。此時由于玻璃窗格的存在,swing將無法在完成此項功能,因此我們需要為玻璃窗格組件重載public boolean contains(int x,int y)方法,以便通知swing框架,哪些x,y值不包含在玻璃窗格的攔截范圍之內(nèi),見如下代碼:
    @Override
    public boolean contains(int x, int y) {
        //when none of mouse events exist
        if (getMouseListeners().length == 0 &&
            getMouseMotionListeners().length == 0 &&
            getMouseWheelListeners().length == 0 &&
            getCursor() == Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)) {
            if (image == null) {
                return false;
            } else {
                int imageX = getWidth() - image.getWidth();
                int imageY = getHeight() - image.getHeight();
                
                // if the mouse cursor is on a non-opaque(transparent) pixel, mouse events
                // are allowed
                int inImageX = x - imageX;
                int inImageY = y - imageY;
                
                if (inImageX >= 0 && inImageY >= 0 &&
                    inImageX < image.getWidth() && inImageY < image.getHeight()) {
                    int color = image.getRGB(inImageX, inImageY);
                    //it must be transparent if alpha is 0.
                    return (color >> 24 & 0xFF) > 0;
                }
                return x > imageX && x < getWidth() && y > imageY && y < getHeight();
            }
        }
        return super.contains(x, y);
    }

21.    分層窗格:JLayoutPane組件是swing的一個容器,是一個容納幾個子層的面板,swing框架依賴一個分層窗格以顯示必須橫跨其他組件的特定組件。分層窗格的每一層都通過一個整數(shù)來識別,這個整數(shù)定義為在這個層的堆棧的深度。最大值表示這個堆棧的最高層次,即顯示層的最上方。JLayerPane提供幾個層標識符以便容易的把組件插入到正確的層。
    JLayeredPane.DEFAULT_LAYER = 0;     一般放置按鈕和表格等正規(guī)組件。
    JLayeredPane.PALETTE_LAYER = 100;    一般用于面板和浮動工具欄。
    JLayeredPane.MODAL_LAYER = 200;        模式對話框。
    JLayeredPane.POPUP_LAYER = 300;        顯示彈出式窗口,包括工具提示、組合框下拉列表、框架菜單和上下文菜單。
    JLayeredPane.DRAG_LAYER = 400;        用于顯示拖拽操作過程中的項。
    swing用間隔100的單位設置這些層,以便使用者可以在他們之間容易的插入自己的層而不引起問題。具體插入方法如下:
    private void addLayeredComponent() {
        MyComponent validator = new MyComponent();        
        JLayeredPane layeredPane = getRootPane().getLayeredPane();
        //分層組件需要使用OverlayLayout布局管理器,或者使用自定義的管理器才能讓該層的組件正確的顯示
        layeredPane.setLayout(new OverlayLayout(layeredPane));
        layeredPane.add(validator, (Integer)(JLayeredPane.DEFAULT_LAYER + 50));
    }
    如果JLayeredPane使用了普通的布局管理器,該管理器將不會考慮JLayeredPane中各個組件的層級關系,而是簡單的將他們視為同一層級,并且繼續(xù)按照該管理器既有的布局邏輯管理所有的組件,即便他們位于JLayeredPane的不同層級。
    private void loadImagesInLayers() {
        layeredPane.setLayout(new FlowLayout());
        for (int i = 2; i <= 5; i++) {
            String name = "images/photo" + i + ".jpg";
            URL url = getClass().getResource(name);
            Icon icon = new ImageIcon(url);
            JLabel label = new JLabel(icon);
            layeredPane.add(label,(Integer)(JLayeredPane.DEFAULT_LAYER + (i - 1) * 2));
        }
    }

22.    重繪管理器(RepaintManager):在Swing的框架中只存在一個RepaintManager,可以通過RepaintManager的靜態(tài)方法currentManager獲取,用戶也可以根據(jù)自己的需要自定義一個RepaintManager的子類,同時通過setCurrentManager方法設置新的RepaintManager。該類主要用于攔截所有swing組件通過repaint方法刷新組件的顯示區(qū)域,該類在攔截并處理后,在交給EDT繼續(xù)處理,因此有些特殊的效果需要通過重載RepaintManager才能很好的完成。如下代碼:
    //class ReflectionRepaintManager extends RepaintManager
    private void installRepaintManager() {
        ReflectionRepaintManager manager = new ReflectionRepaintManager();
        RepaintManager.setCurrentManager(manager);
    }

    class ReflectionRepaintManager extends RepaintManager
    {
        //該方法重載自RepaintManagr,當用戶代碼調(diào)用repaint之后,swing框架會將需要重繪的臟區(qū)域
        //傳遞給RepaintManager的addDirtyRegion方法,該方法中將會根據(jù)自己的需要自行擴展臟區(qū)域,
        //之后在通過調(diào)用父類RepaintManager缺省的addDirtyRegion方法,將更新后的重繪區(qū)域重新交給
        //swing的EDT去處理。
        public void addDirtyRegion(JComponent c, int x, int y, int w, int h) {
            Rectangle dirtyRegion = getDirtyRegion(c);
            int lastDeltaX = c.getX();
            int lastDeltaY = c.getY();
            Container parent = c.getParent();
            while (parent instanceof JComponent) {
                if (!parent.isVisible()) {
                    return;
                }
                //如果父類是反射Panel,則將當前需要重繪的區(qū)域直接覆蓋到相應的反射區(qū)域,以便是
                //相應的反射區(qū)域也能和原本需要更新區(qū)域一同更新。
                if (parent instanceof ReflectionPanel) {
                    x += lastDeltaX;
                    y += lastDeltaY;
                    int gap = contentPane.getHeight() - h - y;
                    h += 2 * gap + h;
                    lastDeltaX = lastDeltaY = 0;
                    c = (JComponent)parent;
                }
                lastDeltaX += parent.getX();
                lastDeltaY += parent.getY();
                parent = parent.getParent();
            }
            super.addDirtyRegion(c, x, y, w, h);
        }
    }

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多