探秘JDK 7之二:半透明和任意形狀的窗口
51CTO昨天為大家?guī)砹恕?a >探秘JDK 7:將會出現(xiàn)新的語言特性”,今天我們從Java SE 6u10(Build 12)開始和大家探討一下Java引入的com.sun.awt.AWTUtilities類提供半透明和任意形狀窗口的支持,這是個臨時類,因為6u10不是一個Java SE主版本,沒有提供抽象窗口工具集API(Abstract Window Toolkit API,AWT API),也沒有修改現(xiàn)有API。在JDK 7中,AWTUtilities類將不復存在,AWT類也做了許多改變,以更好地支持半透明和任意形狀的窗口,本文將為大家介紹一下JDK 7中提供的3種半透明窗口,另外也會涉及到任意形狀窗口的介紹。
簡單的半透明窗口
簡單的半透明窗口就是透明度均勻分布的窗口,所有像素的不透明度值都一樣,這個值越小,窗口越透明,達到最小值時,窗口就是完全透明的了,相反,如果值越大則越不透明。
JDK 7給java.awt.Window類增加了public void setOpacity(float opacity)和public float getOpacity()兩個方法來實現(xiàn)簡單的半透明窗口效果,前一個方法需要一個不透明度參數(shù),其取值范圍是0.0(完全透明)-1.0(完全不透明)。
在窗口上調用setOpacity()方法激活其簡單半透明效果,注意指定的參數(shù)值不能小于0.0,也不能大于1.0,否則setOpacity()會拋出一個IllegalArgumentException異常。
如果窗口處于全屏模式且不透明度值小于1.0,setOpacity()方法會拋出一個java.awt.IllegalComponentStateException異常,如果不支持簡單透明度且不透明度值小于1.0,它會拋出UnsupportedOperationException異常。
java.awt.GraphicsDevice類提供了一個public Window getFullScreenWindow()方法確定窗口是否處于全屏模式,這個類也提供了一個public boolean isWindowTranslucencySupported(GraphicsDevice.WindowTranslucency translucencyKind)方法確定當前的顯示設備是否支持簡單透明效果,如果顯示設備支持這個方法參數(shù)指定的半透明效果,isWindowTranslucencySupported()就返回True,對于簡單半透明效果,這個方法的參數(shù)必須是GraphicsDevice.WindowTranslucency.TRANSLUCENT,如下所示:
- GraphicsEnvironment ge;
- ge = GraphicsEnvironment.getLocalGraphicsEnvironment ();
- if (!ge.getDefaultScreenDevice ().
- isWindowTranslucencySupported (GraphicsDevice.WindowTranslucency.TRANSLUCENT))
- {
- System.err.println ("simple translucency isn't supported");
- return;
- }
我創(chuàng)建了一個STDemo程序演示簡單半透明效果,使用滑塊調節(jié)框架窗口的透明度,清單1顯示了這個程序的源代碼。
清單1. STDemo.java
- // STDemo.java
- import java.awt.EventQueue;
- import java.awt.FlowLayout;
- import java.awt.GraphicsDevice;
- import java.awt.GraphicsEnvironment;
- import javax.swing.JFrame;
- import javax.swing.JLabel;
- import javax.swing.JPanel;
- import javax.swing.JSlider;
- import javax.swing.event.ChangeEvent;
- import javax.swing.event.ChangeListener;
- public class STDemo extends JFrame
- {
- public STDemo ()
- {
- super ("Simple Translucency Demo");
- setDefaultCloseOperation (EXIT_ON_CLOSE);
- final JSlider slider = new JSlider (0, 100, 100);
- ChangeListener cl;
- cl = new ChangeListener ()
- {
- public void stateChanged (ChangeEvent ce)
- {
- JSlider source = (JSlider) ce.getSource ();
- STDemo.this.setOpacity (source.getValue ()/100.0f);
- }
- };
- slider.addChangeListener (cl);
- getContentPane ().setLayout (new FlowLayout ());
- getContentPane ().add (new JLabel ("TRANSP"));
- getContentPane ().add (new JPanel () {{ add (slider); }});
- getContentPane ().add (new JLabel ("OPAQUE"));
- getRootPane ().setDoubleBuffered (false);
- pack ();
- setVisible (true);
- }
- public static void main (String [] args)
- {
- Runnable r;
- r = new Runnable ()
- {
- public void run ()
- {
- GraphicsEnvironment ge;
- ge = GraphicsEnvironment.getLocalGraphicsEnvironment ();
- if (!ge.getDefaultScreenDevice ().
- isWindowTranslucencySupported
- (GraphicsDevice.WindowTranslucency.
- TRANSLUCENT))
- {
- System.err.println ("simple translucency isn't "+
- "supported");
- return;
- }
- new STDemo ();
- }
- };
- EventQueue.invokeLater (r);
- }
- }
上面的代碼創(chuàng)建了一個滑塊,為該組件注冊了一個變化監(jiān)聽器,當滑塊組件移動時,這個組件觸發(fā)變化事件,監(jiān)聽器調用setOpacity(),參數(shù)就使用滑塊的當前值。
上面的代碼使用new JPanel () {{ add (slider); }}創(chuàng)建了一個Swing面板,并在面板上添加了一個滑塊組件,從本質上講,這個快捷方式是先實例化JPanel的一個子類,然后用這個子類的實例初始化滑塊組件。
Swing組件的雙倍緩存可以制造出意想不到的視覺效果,當你拖動滑塊使窗口完全透明時,你將只能看到滑塊本身,但清單1中的代碼通過getRootPane ().setDoubleBuffered (false);禁用了雙倍緩存。
編譯并運行STDemo,移動滑塊就能看到簡單半透明窗口效果了,如圖1所示。
#p#
像素級半透明窗口
像素級半透明允許你控制窗口中每個像素的不透明度,這樣就可以營造出窗口的一部分比另一部分更透明的效果,雖然像素級半透明也可以實現(xiàn)窗口透明度均勻,但與簡單半透明方法比起來,它更占資源。
JDK 7通過修改Window的public void setBackground(Color bgColor)方法,檢查參數(shù)的alpha部分提供像素級半透明效果的支持,如果alpha不等于1.0(窗口不透明),在窗口上繪制每個像素時將會使用alpha值。
真實的半透明級別
繪制像素時使用的真實半透明值也依賴于傳遞給Window的setOpacity()方法的值,以及Window的當前形狀。
如果窗口處于全屏模式且背景色的alpha值小于1.0,這個方法將會拋出IllegalComponentStateException異常,如果不支持像素級半透明且alpha值小于1.0,將會拋出UnsupportedOperationException異常。
為了避免后一個異常,調用GraphicsDevice's isWindowTranslucencySupported()方法時,參數(shù)必須使用GraphicsDevice.WindowTranslucency.PERPIXEL_TRANSLUCENT,并檢查返回的值,如下所示:
- GraphicsEnvironment ge;
- ge = GraphicsEnvironment.getLocalGraphicsEnvironment ();
- if (!ge.getDefaultScreenDevice ().
- isWindowTranslucencySupported (GraphicsDevice.WindowTranslucency.PERPIXEL_TRANSLUCENT))
- {
- System.err.println ("per-pixel translucency isn't supported");
- return;
- }
另外,確定窗口本身是否支持像素級半透明效果也很重要,可調用java.awt.GraphicsConfiguration's public Boolean isTranslucencyCapable()方法來判斷,如果支持的話,這個方法返回Ture,如下所示:
- / The following code fragment continues from the previous code fragment, but assumes that
- // the current class is a descendent of java.awt.Window.
- if (!getGraphicsConfiguration ().isTranslucencyCapable ())
- {
- System.err.println ("per-pixel translucency not in effect for this graphics configuration");
- System.exit (0);
- }
如果你想確定當前背景色的alpha,可調用Window的public Color getBackground()方法,你也可以調用新的public boolean isOpaque()方法確定窗口當前是否是不透明的(返回True)。
我創(chuàng)建了一個PPTDemo程序演示像素級半透明窗口,清單2顯示了它的代碼,因為這個窗口是未加裝飾的,你需要點擊它的關閉按鈕來終止它。
清單2. PPTDemo.java
- // PPTDemo.java
- import java.awt.Color;
- import java.awt.Dimension;
- import java.awt.EventQueue;
- import java.awt.GradientPaint;
- import java.awt.Graphics;
- import java.awt.Graphics2D;
- import java.awt.GraphicsDevice;
- import java.awt.GraphicsEnvironment;
- import java.awt.event.ActionEvent;
- import java.awt.event.ActionListener;
- import javax.swing.Box;
- import javax.swing.BoxLayout;
- import javax.swing.JButton;
- import javax.swing.JFrame;
- import javax.swing.JPanel;
- public class PPTDemo extends JFrame
- {
- public PPTDemo ()
- {
- super ("Per-Pixel Translucency Demo");
- JPanel gradPanel = new JPanel ()
- {
- // Transparent red
- Color colorA = new Color (255, 0, 0, 0);
- // Solid red
- Color colorB = new Color (255, 0, 0, 255);
- protected void paintComponent (Graphics g)
- {
- Graphics2D g2d = (Graphics2D) g;
- GradientPaint gp;
- gp = new GradientPaint (0.0f, 0.0f, colorA,
- 0.0f, getHeight (),
- colorB, true);
- g2d.setPaint (gp);
- g2d.fillRect (0, 0, getWidth (),
- getHeight ());
- }
- };
- gradPanel.setPreferredSize (new Dimension (300, 200));
- gradPanel.setLayout (new BoxLayout (gradPanel, BoxLayout.Y_AXIS));
- JButton btnClose = new JButton ("Close");
- ActionListener al;
- al = new ActionListener ()
- {
- public void actionPerformed (ActionEvent ae)
- {
- System.exit (0);
- }
- };
- btnClose.addActionListener (al);
- btnClose.setAlignmentX (0.5f);
- gradPanel.add (Box.createVerticalGlue ());
- gradPanel.add (btnClose);
- gradPanel.add (Box.createVerticalGlue ());
- setContentPane (gradPanel);
- if (!getGraphicsConfiguration ().isTranslucencyCapable ())
- {
- System.err.println ("per-pixel translucency not in effect for "+
- "this graphics configuration");
- System.exit (0);
- }
- setBackground (new Color (0, 0, 0, 0)); // Achieve per-pixel
- // translucency.
- pack ();
- setLocationRelativeTo (null);
- setVisible (true);
- }
- public static void main (String [] args)
- {
- Runnable r;
- r = new Runnable ()
- {
- public void run ()
- {
- GraphicsEnvironment ge;
- ge = GraphicsEnvironment.getLocalGraphicsEnvironment ();
- if (!ge.getDefaultScreenDevice ().
- isWindowTranslucencySupported
- (GraphicsDevice.WindowTranslucency.
- PERPIXEL_TRANSLUCENT))
- {
- System.err.println ("per-pixel translucency isn't "+
- "supported");
- return;
- }
- new PPTDemo ();
- }
- };
- EventQueue.invokeLater (r);
- }
- }
上面的代碼顯示了用JPanel類創(chuàng)建了兩個java.awt.Color對象,alpha值為0時透明,為255時不透明,它的paintComponent()方法和java.awt.GradientPaint一起給面板的表面涂上了一層梯度式alpha值。
后面的代碼將這個面板作為框架窗口的內容面板,并驗證窗口的圖形配置是否支持像素級半透明,最后調用setBackground()方法開啟像素級半透明窗口效果。
調整窗口的大小后,清單2中的代碼調用setLocationRelativeTo(null)讓面板在屏幕上居中,接著調用setVisible(true)方法控制面板的半透明顯示:上方透明,下方不透明,中間半透明,如圖2所示。
#p#
像素級透明和任意形狀的窗口
像素級透明和像素級半透明類似,這種半透明模式主要用于任意形狀窗口中的內容。
任意形狀窗口是未加裝飾的窗口,其外觀與特定幾何形狀一致(如圓,圓角矩形等),形狀外的像素是透明的,在這些像素上點擊才顯示背景。
JDK 7通過向Window增加public void setShape(Shape shape)和public Shape getShape()方法支持像素級透明和任意形狀的窗口,向前一個方法傳遞一個java.awt.Shape實例給當前窗口指定一個形狀。
如果窗口處于全屏模式且傳遞來一個非空形狀時,setShape()方法會拋出IllegalComponentStateException異常,如果不支持像素級透明且傳遞來一個非空形狀時,拋出UnsupportedOperationException異常。
為了避免后一個異常,調用GraphicsDevice's isWindowTranslucencySupported()方法時,參數(shù)必須使用raphicsDevice.WindowTranslucency.PERPIXEL_TRANSPARENT,并檢查返回的值,如下所示:
- GraphicsEnvironment ge;
- ge = GraphicsEnvironment.getLocalGraphicsEnvironment ();
- if (!ge.getDefaultScreenDevice ().
- isWindowTranslucencySupported (GraphicsDevice.WindowTranslucency.PERPIXEL_TRANSPARENT))
- {
- System.err.println ("per-pixel transparency isn't supported");
- return;
- }
我創(chuàng)建了一個PPTSWDemo程序演示像素級透明和任意形狀的窗口,我們將清單2中顯示出來的圓角矩形改成橢圓形狀,清單3顯示了這個程序的源代碼。
清單3. PPTSWDemo.java
- // PPTSWDemo.java
- import java.awt.Color;
- import java.awt.Dimension;
- import java.awt.EventQueue;
- import java.awt.GradientPaint;
- import java.awt.Graphics;
- import java.awt.Graphics2D;
- import java.awt.GraphicsDevice;
- import java.awt.GraphicsEnvironment;
- import java.awt.Shape;
- import java.awt.event.ActionEvent;
- import java.awt.event.ActionListener;
- import java.awt.geom.Ellipse2D;
- import javax.swing.Box;
- import javax.swing.BoxLayout;
- import javax.swing.JButton;
- import javax.swing.JFrame;
- import javax.swing.JPanel;
- public class PPTSWDemo extends JFrame
- {
- public PPTSWDemo ()
- {
- super ("Per-Pixel Transparency and Shaped Window Demo");
- setUndecorated (true); // Avoid decorated window artifacts.
- JPanel gradPanel = new JPanel ()
- {
- // Solid white
- Color colorA = new Color (255, 255, 255);
- // Solid red
- Color colorB = new Color (255, 0, 0);
- protected void paintComponent (Graphics g)
- {
- Graphics2D g2d = (Graphics2D) g;
- GradientPaint gp;
- gp = new GradientPaint (0.0f, 0.0f, colorA,
- 0.0f, getHeight (),
- colorB, true);
- g2d.setPaint (gp);
- g2d.fillRect (0, 0, getWidth (),
- getHeight ());
- }
- };
- gradPanel.setPreferredSize (new Dimension (300, 200));
- gradPanel.setLayout (new BoxLayout (gradPanel, BoxLayout.Y_AXIS));
- JButton btnClose = new JButton ("Close");
- ActionListener al;
- al = new ActionListener ()
- {
- public void actionPerformed (ActionEvent ae)
- {
- System.exit (0);
- }
- };
- btnClose.addActionListener (al);
- btnClose.setAlignmentX (0.5f);
- gradPanel.add (Box.createVerticalGlue ());
- gradPanel.add (btnClose);
- gradPanel.add (Box.createVerticalGlue ());
- setContentPane (gradPanel);
- pack ();
- setShape (new Ellipse2D.Float (0, 0, getWidth (), getHeight ()));
- setLocationRelativeTo (null);
- setVisible (true);
- }
- public static void main (String [] args)
- {
- Runnable r;
- r = new Runnable ()
- {
- public void run ()
- {
- GraphicsEnvironment ge;
- ge = GraphicsEnvironment.getLocalGraphicsEnvironment ();
- if (!ge.getDefaultScreenDevice ().
- isWindowTranslucencySupported
- (GraphicsDevice.WindowTranslucency.
- PERPIXEL_TRANSPARENT))
- {
- System.err.println ("per-pixel transparency isn't "+
- "supported");
- return;
- }
- new PPTSWDemo ();
- }
- };
- EventQueue.invokeLater (r);
- }
- }
在驗證了支持像素級透明后,清單3執(zhí)行setShape (new Ellipse2D.Float (0, 0, getWidth (), getHeight ()));將框架窗口改成橢圓形,效果如圖3所示。
遺憾的是,任意形狀窗口的邊緣有鋸齒,也許等到JDK 7正式發(fā)布,使用像素級半透明和抗鋸齒功能可以解決掉這個問題。
#p#
半透明聯(lián)合像素級透明和任意形狀窗口
你可以聯(lián)合像素級半透明(或簡單半透明)和像素級透明實現(xiàn)一個半透明的任意形狀窗口,我創(chuàng)建了一個CTSWDemo程序演示這是可能的,清單4中的代碼擴展了前面的例子,包括了像素級半透明代碼。
清單 4. CTSWDemo.java
- // CTSWDemo.java
- import java.awt.Color;
- import java.awt.Dimension;
- import java.awt.EventQueue;
- import java.awt.GradientPaint;
- import java.awt.Graphics;
- import java.awt.Graphics2D;
- import java.awt.GraphicsDevice;
- import java.awt.GraphicsEnvironment;
- import java.awt.Shape;
- import java.awt.event.ActionEvent;
- import java.awt.event.ActionListener;
- import java.awt.geom.Ellipse2D;
- import javax.swing.Box;
- import javax.swing.BoxLayout;
- import javax.swing.JButton;
- import javax.swing.JFrame;
- import javax.swing.JPanel;
- public class CTSWDemo extends JFrame
- {
- public CTSWDemo ()
- {
- super ("Combined Translucency with Per-Pixel Transparency and Shaped "+
- "Window Demo");
- setUndecorated (true); // Avoid decorated window artifacts.
- JPanel gradPanel = new JPanel ()
- {
- Color colorA = new Color (255, 0, 0, 0);
- Color colorB = new Color (255, 0, 0, 255);
- protected void paintComponent (Graphics g)
- {
- Graphics2D g2d = (Graphics2D) g;
- GradientPaint gp;
- gp = new GradientPaint (0.0f, 0.0f, colorA,
- 0.0f, getHeight (),
- colorB, true);
- g2d.setPaint (gp);
- g2d.fillRect (0, 0, getWidth (),
- getHeight ());
- }
- };
- gradPanel.setPreferredSize (new Dimension (300, 200));
- gradPanel.setLayout (new BoxLayout (gradPanel, BoxLayout.Y_AXIS));
- JButton btnClose = new JButton ("Close");
- ActionListener al;
- al = new ActionListener ()
- {
- public void actionPerformed (ActionEvent ae)
- {
- System.exit (0);
- }
- };
- btnClose.addActionListener (al);
- btnClose.setAlignmentX (0.5f);
- gradPanel.add (Box.createVerticalGlue ());
- gradPanel.add (btnClose);
- gradPanel.add (Box.createVerticalGlue ());
- setContentPane (gradPanel);
- if (!getGraphicsConfiguration ().isTranslucencyCapable ())
- {
- System.err.println ("per-pixel translucency not in effect for this "+
- "graphics configuration");
- System.exit (0);
- }
- setBackground (new Color (0, 0, 0, 0)); // Achieve per-pixel
- // translucency.
- pack ();
- setShape (new Ellipse2D.Float (0, 0, getWidth (), getHeight ()));
- setLocationRelativeTo (null);
- setVisible (true);
- }
- public static void main (String [] args)
- {
- Runnable r;
- r = new Runnable ()
- {
- public void run ()
- {
- GraphicsEnvironment ge;
- ge = GraphicsEnvironment.getLocalGraphicsEnvironment ();
- if (!ge.getDefaultScreenDevice ().
- isWindowTranslucencySupported
- (GraphicsDevice.WindowTranslucency.
- PERPIXEL_TRANSLUCENT))
- {
- System.err.println ("per-pixel translucency isn't "+
- "supported");
- return;
- }
- if (!ge.getDefaultScreenDevice ().
- isWindowTranslucencySupported
- (GraphicsDevice.WindowTranslucency.
- PERPIXEL_TRANSPARENT))
- {
- System.err.println ("per-pixel transparency isn't "+
- "supported");
- return;
- }
- new CTSWDemo ();
- }
- };
- EventQueue.invokeLater (r);
- }
- }
上面的梯度代碼在創(chuàng)建Color對象時再次使用了alpha值,如圖4所示,但半透明紅色梯度現(xiàn)在只占用了橢圓形部分,而不是整個窗口。
圖 4 setBackground()方法調用允許橢圓形內用紅色半透明呈梯度渲染
小結
JDK 7對半透明和任意形狀窗口的支持使得創(chuàng)建富有新意的UI更為容易,希望正式發(fā)布時會包含軟剪裁或其它可移除邊緣鋸齒的技術。下一篇文章將會介紹JDK 7中新出現(xiàn)的JLayer Swing組件。
【編輯推薦】