- 删除installUI()设置的边框。
- 删除installUI()设置的布局管理器。
- 删除installUI()添加的组件。
- 删除installUI()添加的事件/模型处理器。
- 删除installUI()安装的特定于LAF的键盘动作。
- 将任何实例化的数据设置为空(允许GC进行垃圾收集)。
比如,为了取消前面例子中的操作,uninstall()方法代码应该如下:
public void uninstallUI(JComponent c)
{
AbstractButton b =
(AbstractButton)c;
// Uninstall
listeners
c.removeMouseListener(mouseListener);
c.removeMouseMotionListener(mouseListener);
mouseListener =
null;
b.removeChangeListener(changeListener);
changeListener =
null;
}
定义几何布局
在AWT/Swing中,容器的LayoutManager会根据不同算法来布局容器的组件,即所谓容器层次结构的“有效化(validation)”。典型的LayoutManager会查询其内部组件的preferredSize属性(根据具体算法有时还会查询minimumSize和/或maximumSize),目的是精确确定组件位置、设置组件大小。LAF通常需要指定某些组件的几何属性,ComponentUI为此提供了以下方法:
public
Dimension getPreferredSize(JComponent
c)
public
Dimension getMinimumSize(JComponent c)
public
Dimension getMaximumSize(JComponent c)
public
boolean contains(JComponent c, int x, int
y)
如果应用程序对这些几何属性没有明确设置,那么JComponent中的对应方法(LayoutManager在validation界面时调用这些方法)会简单将方法代理给相应UI对象,下面是JComponent.getPreferredSize()的方法实现:
public
Dimension getPreferredSize() {
if (preferredSize != null)
{
return
preferredSize;
}
Dimension size =
null;
if (ui != null)
{
size =
ui.getPreferredSize(this);
}
return (size != null) ? size
:
super.getPreferredSize();
}
尽管所有组件的边界是一个Rectangle对象,但可以通过覆盖java.awt.Component继承的contains()方法来模拟非矩形组件(contains()方法用来确定鼠标事件的点击位置)。不像其他Swing的几何属性,UI
delegate定义自己的contains()方法,Jcomponent的contains()方法代理给它完成:
public
boolean contains(JComponent c, int x, int y)
{
return (ui != null) ?
ui.contains(this, x, y) : super.contains(x,
y);
}
因此UI
delegate对象可以通过特定的contains()实现提供非矩形的“感觉”(比如希望用MyButtonUI类实现一个圆角按钮)。
重画
最后,UI
delegate对象须实现当前LAF下的组件重画,为此ComponentUI定义有如下方法:
public
void paint(Graphics g, JComponent c)
public
void update(Graphics g, JComponent c)
JComponent.paintComponent()方法负责组件重画过程的代理工作:
protected
void paintComponent(Graphics g) {
if (ui != null)
{
Graphics scratchGraphics =
SwingGraphics.createSwingGraphics(g.create());
try
{
ui.update(scratchGraphics, this);
}
finally {
scratchGraphics.dispose();
}
}
}
同AWT中使用的方法类似,UI
delegate对象的update()方法清除背景(如果不透明),接着激活自己的paint()方法,最后由paint()方法渲染出组件的内容。我们在前面的文章中已经提到过这段代码:
public
void update(Graphics g, JComponent c) {
if (c.isOpaque())
{
g.setColor(c.getBackground());
g.fillRect(0, 0,
c.getWidth(),c.getHeight());
}
paint(g, c);
}
无状态代理及有状态代理
所有ComponentUI的方法的参数都有一个Jcomponent对象。这种约定俗成的做法可用来实现无状态的UI
delegate对象。代理对象可通过查询组件来获得信息。无状态UI delegate的实现,允许单个UI
delegate实例被所有此组件实例共享,极大减少了实例化的代理对象。
ComponengUI定义了一个静态方法,返回代理实例:
public
static ComponentUI createUI(JComponent
c)
此方法的具体实现决定了代理是否是无状态的,组件通过激活UIManager.getUI()方法来创建UI
delegate,而getUI()激活该UI
delegate的静态方法createUI()来产生实例。这两种类型的代理对象在Swing的LAF实现中都有,比如Swing的BasicButtonUI实现了无状态的代理:
// Shared
UI object
protected
static ButtonUI buttonUI;
public
static ComponentUI createUI(JComponent
c)
if(buttonUI == null)
{
buttonUI = new BasicButtonUI();
}
return
buttonUI;
}
而Swing的BasicTabbedPaneUI实现了有状态的代理:
public
static ComponentUI createUI(JComponent
c){
return new
BasicTabbedPaneUI();
}
LAF总结
Swing可插拔外观的功能很强大,但也是很复杂。其设计目的是,供少数需要实现新外观的开发者使用。应用程序开发者只需要理解这种机制的能力,以便决定支持外观的策略,是使用单一外观还是支持用户配置的多外观。Swing的UIManager为应用程序在这一层的管理提供了编程接口。你如果需要定义一套自己的外观,在编写程序前理解这些基础知识很重要的。
到此为止,Swing体系结构中的UI
Delegate部分已经讲完了。明天继续讨论Renderer和Editor机制。