About Custom Renderers

By creating your own custom look and feel that overrides the ADF UIX default styles and icons, you can significantly alter the appearance of your UIX application. In some cases, however, style and icon customization is not sufficient. If you need complete control over the appearance of the UIX components, you might want to consider writing a custom Renderer.

A Renderer is an object that is responsible for generating the content associated with a UIX component. See the "Extending ADF UIX" chapter of the ADF UIX Developer's Guide for information about defining your own Renderers for your own custom components. In this topic, we will learn how to replace a Renderer for one of the built-in UIX components.

When creating a Renderer for your custom look and feel, you may choose to implement your Renderer using one of two techniques: Your Renderer may either be implemented as a Java class or as a UIX XML template (UIT).

Java-based Renderers provide the greatest flexibility, but at the cost of having to write Java code, which may not appeal to all look and feel implementors. Another issue to be aware of when writing Java-based Renderers is that the Renderer API itself will very likely change once JavaServer Faces is adopted. Currently, UIX defines its own Renderer interface. In the future, the concept of a Renderer will be defined by JavaServer Faces. This means that Java-based Renderer implementations may need to be rewritten for the JavaServer Faces API, whereas template-based Renderers are likely to be more portable (or at least are likely to have a more automated migration path.)

Java-based Renderer implementation

Perhaps the most likely component to require a custom Renderer is the pageLayout component, as the desired page layout is likely to change from site to site. The following sample shows a trivial reimplementation of the pageLayout Renderer. For the sake of simplicity, we only handle two of the pageLayout named children: tabs and pageHeader. Obviously, a real world working pageLayout Renderer would need to do quite a bit more work, as the pageLayout component supports a large number of other named children and attributes that are not honored by this small sample.

For the purposes of this sample, we implement the pageLayout as a single HTML table with three rows: one row for the tabs, one row for the pageHeader, and a final row for the rest of the pageLayout indexed children. Here is a sample Java-based Renderer that implements this layout:


Example:

import java.io.IOException;
import oracle.cabo.ui.ElementRenderer;
import oracle.cabo.ui.RenderingContext;
import oracle.cabo.ui.UIConstants;
import oracle.cabo.ui.UINode;
import oracle.cabo.ui.io.OutputMethod;

// A subclass of ElementRenderer which implements a custom
// pageLayout.
public class PageLayoutRenderer extends ElementRenderer
{
  /**
   * Override of ElementRenderer.getElementName()
   */
  protected String getElementName(
    RenderingContext context,
    UINode           node
    )
  {
    // ElementRenderer automatically renders the start/end
    // tags for the element that is returned by getElementName().
    // Our custom pageLayout renders itself in an HTML table
    // element, so that is what we return
    return "table";
  }

  /**
   * Override of ElementRenderer.renderAttributes().
   */
  protected void renderAttributes(
    RenderingContext context,
    UINode           node
    ) throws IOException
  {
    // renderAttributes() is used to render attribute on the
    // element that is returned by getElementName(). Let's
    // remove all spacing/padding/borders from our layout table.
    OutputMethod out = context.getOutputMethod();
    out.writeAttribute("border", "0");
    out.writeAttribute("cellpadding", "0");
    out.writeAttribute("cellspacing", "0");

    // Call super.renderAttributes() just in case the
    // super class has anything to add
    super.renderAttributes(context, node);
  }

  /**
   * Override of ElementRenderer.prerender()
   */
  protected void prerender(
    RenderingContext context,
    UINode           node
    ) throws IOException
  {
    // First, call super.prerender() so that the
    // "table" element will be started.
    super.prerender(context, node);

    // Now, we render some additional contents in our table.
    // Retrieve the OutputMethod.
    OutputMethod out = context.getOutputMethod();

   // Render the tabBar named child in its own table cell
    out.startElement("tr");
    out.startElement("td");

   // Use BaseRenderer.renderNamedChild() to do the actual rendering
   renderNamedChild(context, node, UIConstants.TABS_CHILD);

   out.endElement("td");
   out.endElement("tr");

   // Render the pageHeader named child in its own table cell
   out.startElement("tr");
   out.startElement("td");

   // Use BaseRenderer.renderNamedChild() to do the actual rendering
   renderNamedChild(context, node, UIConstants.PAGE_HEADER_CHILD);

   out.endElement("td");
   out.endElement("tr");
  }
  
  /**
   * Override BaseRenderer.renderContent()
   */
  public void renderContent(
    RenderingContext context,
    UINode           node) 
    throws IOException
  {
    // renderContent() normally just renders all indexed children.
    // However, since we are rendering our contents inside of an
    // HTML table, we need to override these method to place the
    // indexed children inside of their own table row

    // Retrieve the OutputMethod and start the table row/cell
    OutputMethod out = context.getOutputMethod();
    out.startElement("tr");
    out.startElement("td");

    // Render the contents
    super.renderContent(context, node);

    // Close up the contents table cell/row
    out.endElement("td");
    out.endElement("tr");
  }
}    
Once you have implemented your custom Renderer, you need to compile it and install the Renderer class on the class path (e.g. under the WEB-INF/classes directory for your web application). To specify that your Renderer should be used, you must register it in the look and feel configuration file. For example, assuming the above Renderer is installed in the org.example.laf.custom package, the Renderer can be registered using an <renderer> element as shown in the following:

...
  <renderers>
    <renderer name="ui:pageLayout">
      <class name="org.example.laf.custom.PageLayoutRenderer"/>
    </renderer>
  </renderers>
...    

When your custom look and feel is selected as the preferred look and feel, your custom Renderer classes will automatically be instantiated and used in place of the UIX built-in Renderers.

Template-based Renderer implementation

Although writing a custom Java-based Renderer provides a lot of flexibility, many look and feel implementors may prefer to avoid writing Java code. Template-based Renderers provide an option for implementors who prefer a declarative solution. ADF UIX allows UIX XML template files to be registered as Renderers instead of Java classes. When a UIX XML template file is registered as a Renderer, the template is not referenced by page authors directly as is normally the case. Instead, the template is used by UIX to render the corresponding component.

The following sample shows our pageLayout Renderer rewritten using a UIX XML template:


Example:

<?xml version="1.0" encoding="UTF-8"?>
<templateDefinition xmlns="http://xmlns.oracle.com/uix/ui"
                    xmlns:ui="http://xmlns.oracle.com/uix/ui"
                    xmlns:demo="http://www.example.org/demo/templates"
                    targetNamespace="http://www.example.org/demo/templates"
                    localName="pageLayout">
  <content>

    <!-- We place the pageLayout contents in a tableLayout component -->
    <tableLayout>
      <contents>

        <!-- Render the tabs named child in its own row/cell -->
        <rowLayout>
          <contents>
            <cellFormat>
              <contents>
                <rootChild name="tabs"/>
              </contents>
            </cellFormat>
          </contents>
        </rowLayout>

        <!-- Render the pageHeader named child in its own row/cell -->
        <rowLayout>
          <contents>
            <cellFormat>
              <contents>
                <rootChild name="pageHeader"/>
              </contents>
            </cellFormat>
          </contents>
        </rowLayout>

        <!-- Render the pageLayout contents in its own row/cell -->
        <rowLayout>
          <contents>
            <cellFormat>
              <contents>
                <rootChild name="contents"/>
              </contents>
            </cellFormat>
          </contents>
        </rowLayout>
      </contents> 
    </tableLayout>
  </content>
</templateDefinition>    

Notice that the template-based Renderer uses the same constructs as normal UIX XML templates. For example, named children are referenced using the <rootChild> element. Any element/attribute that can be used in a normal UIX XML template can also be used in a template-based Renderer.

As with Java-based Renderers, template-based Renderers must also be registered in the look and feel configuration file. The following sample shows how to register the above template-based Renderer, assuming it is installed in the templates/laf/custom directory under the web application root:


...
  <renderer name="ui:pageLayout">
    <template name="templates/laf/custom/pageLayout.uit"/>
  </renderer>
...    

Another option that may be attractive to Renderer implementors is the ability to embed arbitrary HTML content inside a template. We can simplify the above template quite a bit by switching over to HTML, as shown in the following:


Example:

<?xml version="1.0" encoding="UTF-8"?>
<templateDefinition xmlns="http://xmlns.oracle.com/uix/ui"
                    xmlns:ui="http://xmlns.oracle.com/uix/ui"
                    xmlns:demo="http://www.example.org/demo/templates"
                    targetNamespace="http://www.example.org/demo/templates"
                    localName="pageLayout">
  <content>

    <!-- Place the pageLayout contents in an HTML table element -->
    <table xmlns="http://www.w3.org/TR/REC-html40"
           cellpadding="0" cellspacing="0" border="0">

      <!-- Render the tabs named child in its own row/cell -->
      <tr><td><ui:rootChild name="tabs"/></td></tr>

      <!-- Render the tabs named child in its own row/cell -->
      <tr><td><ui:rootChild name="pageHeader"/></td></tr>

      <!-- Render the pageLayout contents in its own row/cell -->
      <tr><td><ui:rootChild name="contents"/></td></tr>

    </table>

  </content>

</templateDefinition>    

Note that in the above sample we are able to use arbitrary HTML elements by setting the namespace to http://www.w3.org/TR/REC-html40 . This allows us to avoid using the <contents> elements that are required by all UIX components. We can still use elements such as <rootChild> by explicitly specifying a namespace prefix for these elements.


About Custom Style Sheets
About Custom Icons

Creating Custom Look and Feels
Working with ADF UIX Pages
Working with Web Application Design Tools

 

Copyright © 1997, 2004, Oracle. All rights reserved.