转:http://www.dongmian.org/2010/02/01/胡言乱语之lwuit的ui组件代码结构浅析/
LWUIT: https://lwuit.dev.java.net/ LWUIT是一个轻量级JavaME UI工具包。主要的特性包括:类似Swing 的MVC架构,支持多种布局(Layouts),皮肤更换,字体,触摸屏,动画效果,Rich控件,3D集成,Painter,模式对画 框,I18N/L10N等。值得一提的是LWUIT是sun公司主推的一个图形库,在wtk3.0中已经算是内置的库了。
LWUIT中的组件都在com.sun.lwuit这个包下,所有的组件都是要继承Component这个类,这里Component使用了composite 的设计模式。
为了更形象得了解LWUIT的组件体系。可以看下面的UI组件的关系图。
从图一可以看出,Component分为五大类,分别是
1、Container
2、Label
3、List
4、MediaComponent
5、TextArea
Container与其它的四类组件的不同在于它可以存放子节点,其代码可以下面的方法
Container对子组件操作的方法 void addComponent (Object constraints, Component cmp) void addComponent (int index, Object constraints, Component cmp) void addComponent (int index, Component cmp) void replaceAndWait (final Component current, final Component next, final Transition t) void replace (final Component current, final Component next, final Transition t) void removeComponent (Component cmp) void flushReplace () void removeAll ()
那么每个组件的paint方法又在做什么了?
首先看Container这个类,Container的意思是容器,它的paint方法是对它所包含的每个Componet都有去调paintInternal方法。代码如下:
component的paint方法 public void paint(Graphics g) { layoutContainer(); g.translate(getX(), getY()); int size = components.size(); for (int i = 0; i < size; i++) { Component cmp = (Component)components.elementAt(i); cmp.paintInternal(g, false); } g.translate(-getX(), -getY()); }
paintInternal方法中有分有scrollbar和没有scrollbar两种,代码如下:
component的paintInternal方法 final void paintInternal(Graphics g, boolean paintIntersects) { if (!isVisible()) { return; } int oX = g.getClipX(); int oY = g.getClipY(); int oWidth = g.getClipWidth(); int oHeight = g.getClipHeight(); if (bounds.intersects(oX, oY, oWidth, oHeight)) { g.clipRect(getX(), getY(), getWidth(), getHeight()); paintBackground(g); //是否绘制scrollbar if (isScrollable()) { int scrollX = getScrollX(); int scrollY = getScrollY(); g.translate(-scrollX, -scrollY); //注意paint函数的调用 paint(g); g.translate(scrollX, scrollY); if (isScrollVisible) { paintScrollbars(g); } } else { paint(g); } if (isBorderPainted()) { paintBorder(g); } //paint all the intersecting Components above the Component if (paintIntersects && parent != null) { paintIntersectingComponentsAbove(g); } g.setClip(oX, oY, oWidth, oHeight); } }
这里要注意的paint的方法,根据多态性,它会去调用各个Component的真正方法.
Container下的子类都会去调用Compaint的paint方法,而Label的子类,除了Spinner外,paint方法都会去调用类似下面的方法
Label子类的paint方法
UIManager.getInstance().getLookAndFeel().drawXXXX(g);
这里面的drawXXXX代表是对应的组件名,比如说CheckBox,那它调用方法就是
CheckBox的paint方法 UIManager.getInstance().getLookAndFeel().drawCheckBox(g, this);
RadioButton的调用就为
CheckBox的paint方法 UIManager.getInstance().getLookAndFeel().drawRadioButton(g, this);
从这的代码就引出了UIManager这个类,UIManager这个类的作用是为了移植而进行的封装的,使用单例模式,
在UIManager这个类中要注意的方法有两个:
UIManager的关键方法 /** * Returns the currently installed look and feel * * @return the currently installed look and feel */ public LookAndFeel getLookAndFeel(){ return current; } /** * Sets the currently installed look and feel * * @param plaf the look and feel for the application */ public void setLookAndFeel(LookAndFeel plaf){ current.uninstall(); current = plaf; }
这两个方法就是对组件Render的关键,如果我们想自己实现一套组件的表现,那么我们只要实现一个LookAndFeel的子类,然后对通过setLookAndFeel把它传给UIManager,这种做法也是高度封装的。
在UIManager中,默认的LookAndFeel是DefaultLookAndFeel这个类,这也是组件默认的表现方法。
通过上面的分析,这里使用了单例的设计模式和proxy的设计模式,具体流程为:
1、UIManager在程序初始化之前会根据不同的平台去调用不同的LookAndFeel的实例,而各个平台的LookAndFeel类里面实现各种UI组件的绘制方法。这时使用proxy设计模式。2、各种UI组件只要去调UIManager的对应的draw方法就可以了,这时使用的是单例的设计模式。 |
也就是下图
如果一个软件为了可移植性,那么可以参考一下这种实现方法,当然可移植性还有很多方式来实现,如果有更好的方式欢迎发邮件议论,我的邮箱是:
附:从SVN中直接签出的LWUIT代码是适合netbeans的,但是如果使用Eclipse打开LWUIT的代码会出如下错误提示:
The method 方法名 is defined in an inherited type and an enclosing scope
这时只要在出错的代码前加一个“this.”就可以了。例如Form.java中的MenuBar()构造函数中
setLayout(new GridLayout(1, 3));
就会报“defined in an inherited type and an enclosing scope” 异常,只要把代码改成如下就不会报错了。红色部分是后面要添加的
this.setLayout(new GridLayout(1, 3));
一些名词定义
Singleton pattern(单例模式): http://en.wikipedia.org/wiki/Singleton_pattern
Composite pattern: http://en.wikipedia.org/wiki/Composite_pattern
Proxy pattern(代理模式): http://en.wikipedia.org/wiki/Proxy_pattern