5.3.2 JLayer组件和LayerUI类
Java 7引入了一个新的用户界面组件javax.swing.JLayer,可以把它看成覆盖在一个已有组件上的图层。JLayer组件可以对这个已有的组件进行装饰,也可以监听这个组件上发生的事件。JLayer组件可以把对已有组件所做的处理抽象出来,由专门的类来管理,并应用到其他组件上去。比如,希望监听任何组件上的鼠标单击事件,可以把监听事件的这部分逻辑抽象到一个具体的类中。当需要这种行为的时候,只需要对已有的组件应用这个类的行为即可。JLayer组件本身只负责与已有的组件进行绑定,真正的处理工作交由javax.swing.plaf.LayerUI类来完成。LayerUI类中封装了对组件所做的处理行为。在得到一个LayerUI类的对象之后,可以通过JLayer类的对象将其应用到不同的组件上。
LayerUI类主要可以完成两类操作:一类是对已有组件的界面进行修改,另外一类是对已有组件上产生的事件进行处理。在LayerUI类中可以通过覆写paint方法来对已有组件的外观进行修改。代码清单5-5给出了一个LayerUI类的实现。这个LayerUI类所封装是一种通用的高亮显示一个组件的行为。在这个实现中,首先调用父类的paint方法来进行正常的界面绘制,再在组件的边界上绘制一个红色的边框。
代码清单5-5 高亮显示组件的LayerUI类的实现
public class HighlightLayerUI extends LayerUI{
public void paint(Graphics g, JComponent c){
super.paint(g, c);
g.setColor(Color.red);
g.drawRect(0,0,c.getWidth()-1,c.getHeight()-1);
}
}
有了LayerUI类之后,下一步是创建一个JLayer类对象并把LayerUI类对象与一个已有的组件关联起来。这一步并不复杂,只需要用一个已有的组件和LayerUI类的对象作为参数来创建一个新的JLayer类的对象,再把该JLayer类的对象像其他组件一样添加到界面中即可。这种使用方式相当于用JLayer类的对象对已有的组件进行包装。代码清单5-6给出了JLayer类的一个使用示例。这个例子展示了JLayer类的一种实用的应用场景,可以在运行时动态地改变已有组件的外观。当用户的鼠标移动到某个按钮上时,通过JLayer类的对象的setUI方法为这个按钮添加代码清单5-5中的HighlightLayerUI类所封装的行为,完成后会在按钮上加上一个红色的边框;而在鼠标移开的时候,则为按钮设置了默认的LayerUI类的实现,按钮就恢复了默认的外观。这种动态改变外观的能力可以使程序在某些情况下变得更加灵活。
代码清单5-6 高亮显示组件的LayerUI类的使用示例
public void useHighlight(){
final JFrame frame=new JFrame();
frame.setSize(400,300);
frame.setLayout(new GridLayout(2,1));
frame.add(new JLabel("标签"));
JButton button=new JButton("按钮");
final JLayer<?extends Component>layer=new JLayer<>(button);
final LayerUI layerUI=new HighlightLayerUI();
final LayerUI defaultUI=new LayerUI();
frame.add(layer);
button.addMouseListener(new MouseAdapter(){
public void mouseEntered(MouseEvent e){
layer.setUI(layerUI);
}
public void mouseExited(MouseEvent e){
layer.setUI(defaultUI);
}
});
frame.setVisible(true);
}
除了改变外观之外,JLayer类还可以处理它所包装的组件上的事件。当组件上发生事件的时候,LayerUI类的对象可以得到通知,可以获取到该事件的相关信息。通过这个功能,可以监听组件上发生的事件。代码清单5-7给出了一个示例,LayerUI类的installUI方法在LayerUI类的对象被配置到一个JLayer类的对象上时会被调用。在这个方法中,通过JLayer类的setLayerEventMask方法设置了所感兴趣的事件类型,这里设置了只对鼠标相关的事件感兴趣。而uninstallUI方法则在取消JLayer类的对象与对应的LayerUI类的对象的关联时被调用。在这个方法中,要取消在installUI方法中设置的感兴趣的事件类型,并恢复为默认类型。对于相关的事件,在LayerUI类中有相应的方法来进行处理,比如processMouseEvent方法用来处理鼠标事件,processKeyEvent方法用来处理键盘事件。只要在installUI方法中设置了对某类事件感兴趣,当事件发生的时候,LayerUI类中的相关事件处理方法就会被调用。
代码清单5-7 使用LayerUI类来监听组件上发生的事件
public class MouseMonitorLayerUI extends LayerUI{
public void installUI(JComponent c){
super.installUI(c);
JLayer layer=(JLayer)c;
layer.setLayerEventMask(AWTEvent.MOUSE_EVENT_MASK);
}
public void uninstallUI(JComponent c){
super.uninstallUI(c);
JLayer layer=(JLayer)c;
layer.setLayerEventMask(0);
}
public void processMouseEvent(MouseEvent e, JLayer l){
System.out.println(e.paramString());
}
}
需要注意的是,LayerUI类的对象只能在对应JLayer类的对象所包装的组件上的事件发生时得到通知,并不能拦截事件的发生,所以LayerUI类一般用来实现监视相关的功能。