乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

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

       fenyu8 2011-07-17
      1.    main函數(shù)不要通過直接調(diào)用JFrame子類的構(gòu)造來啟動窗體程序,因為main本身并非運(yùn)行于EDT中,因此可能會給UI帶來同步問題,建議使用一下方式運(yùn)行:
       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,用于獲取當(dāng)前顯示設(shè)備的metrics信息,
      6         //然后根據(jù)該信息在創(chuàng)建設(shè)備兼容的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中的設(shè)備覆蓋掉子
      14 //類graphics中的設(shè)置,這樣通過間接的內(nèi)存圖像可以避免該問題。
      15 super.paint(gButton);
      16
      17 //這里必須直接目的graphics的composite屬性,如果只是修改內(nèi)存圖像的composite,將只會是組件的
      18 //內(nèi)存被渲染成指定的alpha值,但是如果是設(shè)置的目的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被凍結(jié)的現(xiàn)象發(fā)生。
             和操作UI相關(guān)的code務(wù)必要放到EDT中調(diào)用,否則容易導(dǎo)致死鎖。
             SwingUtilities.invodeLater(new Runnable()) 可以在EDT之外執(zhí)行該方法,必將和UI操作相關(guān)的coding放到參數(shù)Runnable的實現(xiàn)中。該工具函數(shù)會自動將Runnable投遞到EDT中執(zhí)行。
             SwingUtilities.isEventDispatchThread() 如果當(dāng)前的執(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í)行該任務(wù)的完成,之后該函數(shù)才正常返回。
             非EDT線程可以通過調(diào)用repaint,強(qiáng)制EDT執(zhí)行組件重繪。


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

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

       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是需要被復(fù)制區(qū)域的左上角坐標(biāo),width和height分別表示該區(qū)域的寬度和高度,dx和dy表示相對于此區(qū)域的位置,如果為正值,則表示此區(qū)域的右邊和下邊,如果為負(fù)值,則表示此區(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.    將一個普通的圖像復(fù)制到一個顯示設(shè)備兼容的圖像中,以提高后期渲染操作的性能。
             注:由于顯示設(shè)備兼容圖像的圖像數(shù)據(jù)存儲方式和設(shè)備顯示時的數(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)建設(shè)備兼容的圖像。

       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.  托管圖像:非托管的圖像在設(shè)備顯示時,是從system memory通過總線copy到VRAM的,托管圖像將會在VRAM中創(chuàng)建一個system memory圖像的副本,需要設(shè)備顯示時,直接將VRAM中的副本copy到VRAM中用于顯示,從而避免了通過總線將數(shù)據(jù)從system memory拷貝到VRAM了。
            抑制圖像自動托管的兩個因素:
            1) 通過Image的Raster,調(diào)用dataBuffer方法直接獲取并且操作顯示數(shù)據(jù)時,java 2d將自動關(guān)閉圖像托管,由于此時是外部代碼直接以數(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)的處理是否需要將該圖像設(shè)置為托管圖像。
            由于渲染操作頻繁,致使從system memory到與VRAM中的副本進(jìn)行同步的操作變?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ī)則圖像,需要考慮將中間圖像的底色設(shè)置為透明,以便在copy的過程中只是復(fù)制這個圖形的顏色,而不會復(fù)制這個圖像的背景色,見下例(其中BITMASK為設(shè)置透明背景):

       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子類的構(gòu)造來啟動窗體程序,因為main本身并非運(yùn)行于EDT中,因此可能會給UI帶來同步問題,建議使用一下方式運(yùn)行:

          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,用于獲取當(dāng)前顯示設(shè)備的metrics信息,然后根據(jù)該信息在創(chuàng)建設(shè)備兼容的BufferedImage對象。
                  buttonImage = getGraphicsConfiguration().createCompatibleImage(getWidth(), getHeight());
              }
              Graphics gButton = buttonImage.getGraphics();
              gButton.setClip(g.getClip());
              
              //使用超類中的paint方法將需要顯示的image繪制到該內(nèi)存圖像中,不推薦直接從superclass繪制到g(子類paint方法的graphics對象),因為這樣做可能會將superclass的graphics中的設(shè)備覆蓋掉子類graphics中的設(shè)置,這樣通過間接的內(nèi)存圖像可以避免該問題。
              super.paint(gButton);
              
              //這里必須直接目的graphics的composite屬性,如果只是修改內(nèi)存圖像的composite,將只會是組件的內(nèi)存被渲染成指定的alpha值,但是如果是設(shè)置的目的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被凍結(jié)的現(xiàn)象發(fā)生。
          和操作UI相關(guān)的code務(wù)必要放到EDT中調(diào)用,否則容易導(dǎo)致死鎖。
          SwingUtilities.invodeLater(new Runnable()) 可以在EDT之外執(zhí)行該方法,必將和UI操作相關(guān)的coding放到參數(shù)Runnable的實現(xiàn)中。該工具函數(shù)會自動將Runnable投遞到EDT中執(zhí)行。
          SwingUtilities.isEventDispatchThread() 如果當(dāng)前的執(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í)行該任務(wù)的完成,之后該函數(shù)才正常返回。
          非EDT線程可以通過調(diào)用repaint,強(qiáng)制EDT執(zhí)行組件重繪。
       
      4.    java.util.Timer 定時器中的TimerTask是在EDT之外的獨(dú)立線程中執(zhí)行的,因為不能直接執(zhí)行UI的操作。
          java.swing.Timer 定時器中的任務(wù)是在EDT中執(zhí)行的,因為可以包含直接操作UI的代碼。
              
      5.    打開抗鋸齒:g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
          以下的例子是根據(jù)OS桌面的設(shè)置,為文本的渲染打開抗鋸齒。
              
          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是需要被復(fù)制區(qū)域的左上角坐標(biāo),width和height分別表示該區(qū)域的寬度和高度,dx和dy表示相對于此區(qū)域的位置,如果為正值,則表示此區(qū)域的右邊和下邊,如果為負(fù)值,則表示此區(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.    將一個普通的圖像復(fù)制到一個顯示設(shè)備兼容的圖像中,以提高后期渲染操作的性能。
          注:由于顯示設(shè)備兼容圖像的圖像數(shù)據(jù)存儲方式和設(shè)備顯示時的數(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)建設(shè)備兼容的圖像。
          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.    托管圖像:非托管的圖像在設(shè)備顯示時,是從system memory通過總線copy到VRAM的,托管圖像將會在VRAM中創(chuàng)建一個system memory圖像的副本,需要設(shè)備顯示時,直接將VRAM中的副本copy到VRAM中用于顯示,從而避免了通過總線將數(shù)據(jù)從system memory拷貝到VRAM了。
          抑制圖像自動托管的兩個因素:
          1) 通過Image的Raster,調(diào)用dataBuffer方法直接獲取并且操作顯示數(shù)據(jù)時,java 2d將自動關(guān)閉圖像托管,由于此時是外部代碼直接以數(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)的處理是否需要將該圖像設(shè)置為托管圖像。
          由于渲染操作頻繁,致使從system memory到與VRAM中的副本進(jìn)行同步的操作變?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ī)則圖像,需要考慮將中間圖像的底色設(shè)置為透明,以便在copy的過程中只是復(fù)制這個圖形的顏色,而不會復(fù)制這個圖像的背景色,見下例(其中BITMASK為設(shè)置透明背景):
          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: 其運(yùn)算公式為Ar = As + Ad * (1 - As), Cr = Cs + Cd * (1 - As), 其中Ar為結(jié)果Alpha,As表示源圖像的Alpha,As為目的圖像的Alpha,Cr表示(RGB)中每個通道的結(jié)果值,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. 模糊這個副本的一部分以使它淡出,隨著它遠(yuǎn)離最初的物體。
              GradientPaint mask;
              //目的顏色RGB無關(guān)重要,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)    更巧妙的緩存:當(dāng)繪制一個垂直或者水平漸變時,每一列或者每一行都是相同的,因此可以只是保留一列或者一行的數(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將不斷的檢查是否當(dāng)前的像素位于這個漸變區(qū)域的外面,因此對于成千上萬的像素來說,將會花費(fèi)很多時間。如果使用循環(huán)漸變的方式,java 2d內(nèi)部在渲染的時候?qū)贿M(jìn)行該判斷,從而大大提高了整體的效率,見如下代碼:
          //循環(huán)GradientPaint
          new GradientPaint(new Point(0,0),Color.WHITE,new Point(0,getHeight()),Color.DARK_GRAY,true/*該標(biāo)志表示循環(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).    給當(dāng)前JFrame安裝玻璃窗格,安裝后該玻璃窗格的缺省顯示方式是隱藏顯示,即setVisible(false),如果之前JFrame已經(jīng)使用了玻璃窗格,本次操作只是替換一個新的對象,那么該窗格的visible屬性將和原有窗格的visible屬性保持一致。
          public ApplicationFrame() { //ApplicationFrame為應(yīng)用程序的主窗體,繼承自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)用的方法,以便更新進(jìn)度條的顯示狀態(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.    玻璃窗格中屏蔽輸入事件,上例中繪制的玻璃窗格只是完成了基本的顯示效果,用戶仍然可以操作玻璃窗格覆蓋下的控件,這樣會給用戶帶來非常迷惑的感覺,因此需要屏蔽玻璃窗格覆蓋下的控件獲取來自鼠標(biāo)和鍵盤的事件。
          1).    為玻璃窗格控件自身添加空的鼠標(biāo)和鍵盤的監(jiān)聽器
          public ProgressGlassPane() {
              // blocks all user input
              addMouseListener(new MouseAdapter() { });
              addMouseMotionListener(new MouseMotionAdapter() { });
              addKeyListener(new KeyAdapter() { });
          }
          
          2).    以上操作只是較好的屏蔽了鼠標(biāo)事件,但是對于鍵盤事件,由于swing將鍵盤事件直接發(fā)送到當(dāng)前聚焦的控件,因此如果有一組控件已經(jīng)獲取了焦點,它仍然可以收到鍵盤按鍵事件,甚至可以通過tab或ctrl+tab在各個控件之間切換焦點。要完成該功能,需要在玻璃窗體變成可見時調(diào)用requestFocusInWindow()以奪取焦點,因此該段代碼仍然需要放在該對象的構(gòu)造函數(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鍵將焦點傳入玻璃窗格覆蓋的控件中,因此需要在構(gòu)造函數(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ū)域的鼠標(biāo)事件,比如在一個完全透明的窗格中的左下角繪制一個公司的logo,其他部分則完全透明,此時,如果用戶將鼠標(biāo)放到玻璃窗格下面的控件上方時,由于JFrame的最頂層組件是玻璃窗格,因此他攔截了鼠標(biāo)光標(biāo)的顯示效果,比如其下擺放了一組輸入框,如果沒有玻璃窗格,那么當(dāng)鼠標(biāo)停留在控件上方時,swing會根據(jù)實際控件的類型更新鼠標(biāo)光標(biāo)的形狀。此時由于玻璃窗格的存在,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提供幾個層標(biāo)識符以便容易的把組件插入到正確的層。
          JLayeredPane.DEFAULT_LAYER = 0;     一般放置按鈕和表格等正規(guī)組件。
          JLayeredPane.PALETTE_LAYER = 100;    一般用于面板和浮動工具欄。
          JLayeredPane.MODAL_LAYER = 200;        模式對話框。
          JLayeredPane.POPUP_LAYER = 300;        顯示彈出式窗口,包括工具提示、組合框下拉列表、框架菜單和上下文菜單。
          JLayeredPane.DRAG_LAYER = 400;        用于顯示拖拽操作過程中的項。
          swing用間隔100的單位設(shè)置這些層,以便使用者可以在他們之間容易的插入自己的層而不引起問題。具體插入方法如下:
          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中各個組件的層級關(guān)系,而是簡單的將他們視為同一層級,并且繼續(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方法設(shè)置新的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,當(dāng)用戶代碼調(diào)用repaint之后,swing框架會將需要重繪的臟區(qū)域
              //傳遞給RepaintManager的addDirtyRegion方法,該方法中將會根據(jù)自己的需要自行擴(kuò)展臟區(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,則將當(dāng)前需要重繪的區(qū)域直接覆蓋到相應(yīng)的反射區(qū)域,以便是
                      //相應(yīng)的反射區(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)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多