TA的每日心情 | 开心 2021-3-12 23:18 |
---|
签到天数: 2 天 [LV.1]初来乍到
|
从java SE 6u10(Build 12)开始,Java引入了com.sun.awt.AWTUtilities类提供半透明和任意形状窗口的支持,这个是个临时类,因为6u10不是一个 Java SE主版本,没有提供抽象窗口工具集API(Abstract Window Toolkit API,AWT API),也没有修改现有API。在JDK 7中,AWTUtilities类将不复存在,AWT类也做了许多改变,以更好地支持半透明和任意形状的窗口,本文将为大家介绍一下JDK 7中提供的3种半透明窗口,另外也会涉及到任意形状窗口的介绍。 简单的半透明窗口 简单的半透明窗口就是透明度均匀分布的窗口,所有像素的不透明度值都一样,这个值越小,窗口越透明,达到最小值时,窗口就是完全透明的了,相反,如果值越大则越不透明。 JDK 7给java.awt.Window类增加了public void setOpacity(float opacity)和public float getOpacity()两个方法来实现简单的半透明窗口效果,前一个方法需要一个不透明度参数,其取值范围是0.0(完全透明)-1.0(完全不透 明)。
在窗口上调用setOpacity()方法激活其简单半透明效果,注意指定的参数值不能小于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)方法确定当前的显示设备是否支持简单透明效果,如果显示设备支持这个方法参数指定的半透明效 果,isWindowTranslucencySupported()就返回True,对于简单半透明效果,这个方法的参数必须是 GraphicsDevice.WindowTranslucency.TRANSLUCENT,如下所示:
#div_code img{border:0px;}
GraphicsEnvironment ge;
ge
=
GraphicsEnvironment.getLocalGraphicsEnvironment ();
if
(
!
ge.getDefaultScreenDevice ().
isWindowTranslucencySupported (GraphicsDevice.WindowTranslucency.TRANSLUCENT))
{
System.err.println (
"
simple translucency isn"t supported
"
);
return
;
}
我创建了一个STDemo程序演示简单半透明效果,使用滑块调节框架窗口的透明度,清单1显示了这个程序的源代码。
清单1. STDemo.java
#div_code img{border:0px;}
//
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);
}
}
上面的代码创建了一个滑块,为该组件注册了一个变化监听器,当滑块组件移动时,这个组件触发变化事件,监听器调用setOpacity(),参数就使用滑块的当前值。
上面的代码使用new JPanel () {{ add (slider); }}创建了一个Swing面板,并在面板上添加了一个滑块组件,从本质上讲,这个快捷方式是先实例化JPanel的一个子类,然后用这个子类的实例初始化滑块组件。
Swing组件的双倍缓存可以制造出意想不到的视觉效果,当你拖动滑块使窗口完全透明时,你将只能看到滑块本身,但清单1中的代码通过getRootPane ().setDoubleBuffered (false);禁用了双倍缓存。
编译并运行STDemo,移动滑块就能看到简单半透明窗口效果了,如图1所示。
图 1 窗口及其内容全部处于均匀透明的状态
像素级半透明窗口
像素级半透明允许你控制窗口中每个像素的不透明度,这样就可以营造出窗口的一部分比另一部分更透明的效果,虽然像素级半透明也可以实现窗口透明度均匀,但与简单半透明方法比起来,它更占资源。
JDK 7通过修改Window的public void setBackground(Color bgColor)方法,检查参数的alpha部分提供像素级半透明效果的支持,如果alpha不等于1.0(窗口不透明),在窗口上绘制每个像素时将会使 用alpha值。
真实的半透明级别
绘制像素时使用的真实半透明值也依赖于传递给Window的setOpacity()方法的值,以及Window的当前形状。
如果窗口处于全屏模式且背景色的alpha值小于1.0,这个方法将会抛出IllegalComponentStateException异常,如果不 支持像素级半透明且alpha值小于1.0,将会抛出UnsupportedOperationException异常。
为了避免后一 个异常,调用GraphicsDevice"s isWindowTranslucencySupported()方法时,参数必须使用 GraphicsDevice.WindowTranslucency.PERPIXEL_TRANSLUCENT,并检查返回的值,如下所示:
#div_code img{border:0px;}
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,如下所示
#div_code img{border:0px;}
/
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)。
我创建了一个PPTDemo程序演示像素级半透明窗口,清单2显示了它的代码,因为这个窗口是未加装饰的,你需要点击它的关闭按钮来终止它。
清单2. PPTDemo.java
#div_code img{border:0px;}
//
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类创建了两个java.awt.Color对象,alpha值为0时透明,为255时不透明,它的paintComponent()方法和java.awt.GradientPaint一起给面板的表面涂上了一层梯度式alpha值。
后面的代码将这个面板作为框架窗口的内容面板,并验证窗口的图形配置是否支持像素级半透明,最后调用setBackground()方法开启像素级半透明窗口效果。
调整窗口的大小后,清单2中的代码调用setLocationRelativeTo(null)让面板在屏幕上居中,接着调用setVisible(true)方法控制面板的半透明显示:上方透明,下方不透明,中间半透明,如图2所示。
图 2 从上到下,透明度不断减小
像素级透明和任意形状的窗口
像素级透明和像素级半透明类似,这种半透明模式主要用于任意形状窗口中的内容。
任意形状窗口是未加装饰的窗口,其外观与特定几何形状一致(如圆,圆角矩形等),形状外的像素是透明的,在这些像素上点击才显示背景。
JDK 7通过向Window增加public void setShape(Shape shape)和public Shape getShape()方法支持像素级透明和任意形状的窗口,向前一个方法传递一个java.awt.Shape实例给当前窗口指定一个形状。
如果窗口处于全屏模式且传递来一个非空形状时,setShape()方法会抛出IllegalComponentStateException异常,如果不支持像素级透明且传递来一个非空形状时,抛出UnsupportedOperationException异常。
为了避免后一个异常,调用GraphicsDevice"s isWindowTranslucencySupported()方法时,参数必须使用 raphicsDevice.WindowTranslucency.PERPIXEL_TRANSPARENT,并检查返回的值,如下所示:
#div_code img{border:0px;}
GraphicsEnvironment ge;
ge
=
GraphicsEnvironment.getLocalGraphicsEnvironment ();
if
(
!
ge.getDefaultScreenDevice ().
isWindowTranslucencySupported (GraphicsDevice.WindowTranslucency.PERPIXEL_TRANSPARENT))
{
System.err.println (
"
per-pixel transparency isn"t supported
"
);
return
;
}
我创建了一个PPTSWDemo程序演示像素级透明和任意形状的窗口,我们将清单2中显示出来的圆角矩形改成椭圆形状,清单3显示了这个程序的源代码。
清单3. PPTSWDemo.java
#div_code img{border:0px;}
//
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执行setShape (new Ellipse2D.Float (0, 0, getWidth (), getHeight ()));将框架窗口改成椭圆形,效果如图3所示。
图 3 梯度式透明现在限制在椭圆范围内
遗憾的是,任意形状窗口的边缘有锯齿,也许等到JDK 7正式发布,使用像素级半透明和抗锯齿功能可以解决掉这个问题。
半透明联合像素级透明和任意形状窗口
你可以联合像素级半透明(或简单半透明)和像素级透明实现一个半透明的任意形状窗口,我创建了一个CTSWDemo程序演示这是可能的,清单4中的代码扩展了前面的例子,包括了像素级半透明代码。
清单 4. CTSWDemo.java
#div_code img{border:0px;}
//
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);
}
}
上面的梯度代码在创建Color对象时再次使用了alpha值,如图4所示,但半透明红色梯度现在只占用了椭圆形部分,而不是整个窗口。
图 4 setBackground()方法调用允许椭圆形内用红色半透明呈梯度渲染
小结
JDK 7对半透明和任意形状窗口的支持使得创建富有新意的UI更为容易,希望正式发布时会包含软剪裁或其它可移除边缘锯齿的技术。下一篇文章将会介绍JDK 7中新出现的JLayer Swing组件。
源码下载:http://file.javaxxz.com/2014/11/7/235652859.zip |
|