OperaMasks 2.3及以前版本中构件渲染机制不统一,存在多种渲染方式,一些构件与另一些构件的渲染方式可以差别很大,用户要定义自己的构件时,往往不知道应该参考哪一套机制,或者没明白渲染机制中的原理就盲目参考,导致定义出来的构件基本不可用。
OperaMasks 3.0版本对所有构件的渲染机制进行了统一,提供统一的基类和接口,所有构件渲染器采用同一套机制来写,结构清晰,用户参考任何一个构件的渲染器都可以定义出自己的构件或渲染器。并且3.0版本中极大地减少了Annotation使用,使用渲染的效率也得到了提高。
统一后的继承体系如下图:
其中最上面的Renderer是JSF中规定的所有渲染器的基类,只有继承自这个类才能称得上是渲染器。HtmlBasicRenderer是一个接口,此接口提供渲染出HTML代码的方法。ResourceRenderer是一个接口,它提供渲染JavaScript代码的方法。HtmlBasicRendererBase是所有Render-Kit是Html-Basic的渲染器的基类,它实现了HtmlBasicRenderer和ResourceRenderer接口,因而能在非AJAX情况下构件的渲染HTML代码和JavaScript代码。AjaxRenderer是一个接口,这提供在AJAX情况下进行AJAX渲染的方法。AjaxRendererBase是所有Render-Kit是AJAX的渲染器的基类,因为AJAX渲染器在首次请求时一般也要渲染出一些HTML代码和JavaScript代码,所以它继承自HtmlBasicRendererBase,同时它还实现了一个PartialUpdateRenderer接口。PartialUpdateRenderer接口提供局部渲染(OperaMasks 3.0的一个新特性)相关的方法。
简单说来,我们要定义一个渲染器,只要直接或间接继承自HtmlBasicRendererBase或AjaxRendererBase就可以了。如果要构件工作在AJAX下(即页面中f:view的renderKitId="AJAX"),就继承自AjaxRendererBase。如果构件要工作在Html-Basic下,就继承自HtmlBasicRendererBase。继承之后只需要根据实际需要覆写其中的一些方法就可以了。
我们来看一下这两个渲染器基类中有哪些方法可以覆盖。Html-Basic渲染器中有以下方法:
public void encodeHtmlBegin(FacesContext context, UIComponent component) throws IOException;
该方法由HtmlBasicRenderer接口定义,作用是渲染出本构件渲染时所发回的HTML代码的头部,即它会渲染出一些HTML代码,而这些HTML代码应该渲染在它的子构件的HTML代码之前。
public boolean getEncodeHtmlChildren(UIComponent component);
该方法由HtmlBasicRenderer接口定义,作用是标识该构件的子构件的HTML代码渲染是否应该由构件自己来渲染。返回true时说明其子构件的HTML代码渲染过程由该构件接管,子构件就不再渲染了。返回false时说明该构件只渲染自己的HTML代码,至于子构件的HTML代码由子构件自己负责渲染。该方法默认为false。
注意:如果此方法返回false,则下面的encodeHtmlChildren()方法不会执行,即使用户覆盖了那个方法,也不会执行里面的代码。
public void encodeHtmlChildren(FacesContext context, UIComponent component) throws IOException;
该方法由HtmlBasicRenderer接口定义,如果上面的getEncodeHtmlChildren()方法返回true时,此方法渲染出本构件的子构件应该渲染出的所有HTML代码。
注意:如果上面的getEncodeHtmlChildren()方法返回false时,则该方法不会执行。
public void encodeHtmlEnd(FacesContext context, UIComponent component) throws IOException;
该方法由HtmlBasicRenderer接口定义,作用是渲染出本构件渲染时所发回的HTML代码的尾部。即它会渲染出一些HTML代码,而这些代码应该出现在它所有的子构件渲染出的HTML代码的后面。
public String[] getDependedJSPackages(FacesContext context, UIComponent component);
该方法由ResourceRenderer接口定义,作用是指明该构件渲染时需要使用的外部js文件或文件组的别名(别名在resource-dependences.xml文件中定义)。
public String[] getDependedCSSPackages(FacesContext context, UIComponent component);
该方法由ResourceRenderer接口定义,作用是指明该构件渲染时需要使用的外部css文件或文件组的别名(别名在resource-dependences.xml文件中定义)。
public void encodeInitScriptBegin(FacesContext context,ResourceManager rm, UIComponent component) throws IOException;
该方法由ResourceRenderer接口定义,作用是渲染出与本构件相关的初始化JavaScript代码的头部。即它会渲染出一些初始化JavaScript代码(比如声明js变量)。这些代码应该出现在本构件渲染出的JavaScript代码的前面(在父子构件的encodeInitScriptBegin()渲染出的初始化JavaScript代码之后,但是在父构件的encodeResourceBegin()渲染出的JavaScript代码之前)。
public void encodeResourceBegin(FacesContext context,ResourceManager rm, UIComponent component) throws IOException;
该方法由ResourceRenderer接口定义,作用是渲染出与本构件相关的JavaScript代码的头部。即它会渲染出一些JavaScript代码。这些代码应该出现在本构件的子构件渲染出的JavaScript代码的前面(但是在子构件的encodeInitScriptBegin()渲染出的初始化JavaScript代码的后面)。
public boolean getEncodeResourceChildren(UIComponent component);
该方法由ResourceRenderer接口定义,作用是标识该构件的子构件的JavaScript代码渲染是否应该由构件自己来渲染。返回true时说明其子构件的JavaScript代码渲染过程由该构件接管,子构件就不再渲染了。返回false时说明该构件只渲染自己的JavaScript代码,至于子构件的JavaScript代码由子构件自己负责渲染。该方法默认为false。
注意:如果此方法返回false,则下面的encodeResourceChildren()方法不会执行,即使用户覆盖了那个方法,也不会执行里面的代码。
public void encodeResourceChildren(FacesContext context,ResourceManager rm, UIComponent component,boolean isContainer) throws IOException;
该方法由ResourceRenderer接口定义,如果上面的getEncodeResourceChildren()方法返回true时,此方法渲染出本构件的子构件应该渲染出的所有JavaScript代码。
注意:如果上面的getEncodeResourceChildren()方法返回false,,则该方法不会执行。
public void encodeResourceEnd(FacesContext context,ResourceManager rm, UIComponent component) throws IOException;
该方法由ResourceRenderer接口定义,作用是渲染出与本构件相关的JavaScript代码的尾部。即它会渲染出一些JavaScript代码。这些代码应该出现在本构件的子构件渲染出的JavaScript代码的后面(但是在子构件的encodeInitScriptEnd()渲染出的初始化JavaScript代码的前面)。
public void encodeInitScriptEnd(FacesContext context,ResourceManager rm, UIComponent component) throws IOException;
该方法由ResourceRenderer接口定义,作用是渲染出与本构件相关的初始化JavaScript代码的尾部。即它会渲染出一些初始化JavaScript代码。这些代码应该出现在本构件渲染出的JavaScript代码的后面(在父构件的encodeInitScriptEnd()渲染出的初始化JavaScript代码之前,但是在本构件及子构件的encodeResourceEnd()方法渲染出的JavaScript代码之后)。
而且各方法执行的先后顺序与上面的排序一致。我们来看一段代码:
<w:panelBox>
<w:form>
<w:button />
</w:form>
</w:panelBox>其中w:button的getEncodeHtmlChildren()和getEncodeResourceChildren()方法返回的是true,其它构件这两个方法都是返回false,那么在渲染时上面那些方法调用的先后顺序是这样的:
w:panelBox的渲染器的encodeHtmlBegin w:form的渲染器的encodeHtmlBegin w:button的渲染器的encodeHtmlBegin w:button的渲染器的encodeHtmlChildren w:button的渲染器的encodeHtmlEnd w:form的渲染器的encodeHtmlEnd w:panelBox的渲染器的encodeHtmlEnd w:panelBox的encodeInitScriptBegin w:form的渲染器的encodeInitScriptBegin w:button的渲染器的encodeInitScriptBegin w:panelBox的encodeResourceBegin w:form的encodeResourceBegin w:button的encodeResourceBegin w:button的encodeResourceChildren w:button的encodeResourceEnd w:form的encodeResourceEnd w:panelBox的encodeResourceEnd w:button的渲染器的encodeInitScriptEnd w:form的渲染器的encodeInitScriptEnd w:panelBox的encodeInitScriptEnd
我们再看一看AJAX渲染器有哪些方法,AjaxRendererBase类继承自HtmlBasicRendererBase,所以AJAX渲染器也有上面的那些方法。因为Render-Kit="AJAX"的首次渲染是GET请求,属于非AJAX的,与上面的Html-Basic渲染器一样,也执行那些方法,执行的先后顺序也是一样的。
而Render-Kit="AJAX"的非首次请求是POST请求,是AJAX的,它会执行一些AjaxRendererBase中实现自AjaxRenderer接口的方法,同时OperaMasks 3.0中为AJAX渲染器提供局部渲染的功能,AjaxRendererBase中还有一些PartialUpdateRenderer接口实现的方法。具体有以下方法
public void encodeAjaxBegin(FacesContext context, UIComponent component) throws IOException;
该方法由AjaxRenderer接口定义,作用是渲染本构件postback请求时所发回的JavaScript的头部,即它会渲染出一些JavaScript代码,而这些JavaScript代码应该渲染在它的子构件的JavaScript代码之前。
public boolean getEncodeAjaxChildren(UIComponent component);
该方法由AjaxRenderer接口定义,作用是标识该构件的子构件的JavaScript代码渲染是否应该由构件自己来渲染。返回true时说明其子构件的JavaScript代码渲染过程由该构件接管,子构件就不再渲染了。返回false时说明该构件只渲染自己的JavaScript代码,至于子构件的JavaScript代码由子构件自己负责渲染。该方法默认为false。
注意:如果此方法返回false,则下面的encodeAjaxChildren()方法不会执行,即使用户覆盖了那个方法,也不会执行里面的代码。
public void encodeAjaxChildren(FacesContext context, UIComponent component) throws IOException;
该方法由AjaxRenderer接口定义,如果上面的getEncodeAjaxChildren()方法返回true时,此方法渲染出本构件的子构件应该渲染出的所有JavaScript代码。
注意:如果上面的getEncodeAjaxChildren()方法返回false,,则该方法不会执行。
public void encodeAjaxEnd(FacesContext context, UIComponent component) throws IOException;
该方法由AjaxRenderer接口定义,作用是渲染本构件postback请求时所发回的JavaScript的尾部,即它会渲染出一些JavaScript代码,而这些JavaScript代码应该渲染在它的子构件的JavaScript代码之后。
public Map<String, LiteAttributeEncoder> getLiteAttributes();
该方法由PartialUpdateRenderer接口定义,作用标明需要支持局部更新的渲染器实现此方法返回本构件所有支持轻量级更新的属性集合,如果本构件没有任何轻量级更新属性,应返回空集合。
public Set<String> getUnupdatableAttributes();
该方法由PartialUpdateRenderer接口定义,作用标明需要支持局部更新的渲染器实现此方法返回本构件所有不支持AJAX更新的属性集合,如果本构件没有任何不支持AJAX更新的属性,应返回空集合。
public boolean isRepaintable(UIComponent component);
该方法由PartialUpdateRenderer接口定义,作用标识需要支持局部更新的渲染器实现此方法返回本构件是否能够通过repaint更新,默认为true,即默认所有构件都可以repaint。
而且各方法执行的先后顺序为:encodeAjaxBegin()-->getEncodeAjaxChildren()-->encodeAjaxChildren()(如果getEncodeAjaxChildren()返回true才会执行)-->encodeAjaxEnd(),下面三个方法在引擎内部自动调用。还是那段代码:
<w:panelBox>
<w:form>
<w:button />
</w:form>
</w:panelBox>其中w:button的getEncodeAjaxChildren()方法返回的是true,其它构件这个方法都是返回false,那么在AJAX请求的渲染时上面那些方法调用的先后顺序执行这些方法:
w:panelBox的渲染器的encodeAjaxBegin w:form的渲染器的encodeAjaxBegin w:button的渲染器的encodeAjaxBegin w:button的渲染器的encodeAjaxChildren w:button的渲染器的encodeAjaxEnd w:form的渲染器的encodeAjaxEnd w:panelBox的渲染器的encodeAjaxEnd
总之,Html-Basic渲染或AJAX的首次渲染时,构件的渲染器会按顺序执行HtmlBasicRendererBase的各个方法。AJAX的非首次渲染时会按顺序执行这些方法:encodeAjaxBegin()-->getEncodeAjaxChildren()-->encodeAjaxChildren()(如果getEncodeAjaxChildren()返回true才会执行)-->encodeAjaxEnd()。