jsf

One-off ADF component skin appearance

Posted on Updated on

Have you ever wanted to make an ADF Faces Rich Client component have a unique appearance but not change all other instances of that component? You can as long as the things you want to change are not skin properties or skin icons (server-side aspects like properties and icons are not part of the client-side generated style sheet so this client-side solution will not allow you to change them).

Let’s start with an interactive example; the skinning demo pages for the ADF Faces Rich Client:
http://jdevadf.oracle.com/adf-richclient-demo/components/skinningKeys/inputText.jspx

On this page, there is a right-hand side where you can see some example inputText components.  When you click on the checkboxes on the left-hand side of this page, the appearance of those start to change.  However, the inputText for the find field at the top of the page does not change.  This is because of some containment selector definitions in the skin used on this page that confines the custom styling to instances of the inputText that are contained in another component that has a particular style class.  Since the find field is not inside of that container, these custom styles do not apply to that field.

Let’s say your page has a structure like this:

<af:panelGroupLayout id="pgl1" layout="scroll" styleClass="SpecialMarker">
  <af:inputText id="it1" label="Name"/>
</af:panelGroupLayout>

To style that inputText specially in this container, you would create skinning definitions like this:

.SpecialMarker af|inputText {background-color:pink}
.SpecialMarker af|inputText::access-key {color: aqua;}
.SpecialMarker af|inputText::content {background-color:red}
.SpecialMarker af|inputText::label {font-weight: bold}

Alternatively, you could put your marker directly on the inputText like this:

<af:inputText id="it1" label="Name" styleClass="SpecialMarker"/>

and have skinning definitions like this:

af|inputText.SpecialMarker {background-color:pink}
af|inputText.SpecialMarker af|inputText::access-key {color: aqua;}
af|inputText.SpecialMarker af|inputText::content {background-color:red}
af|inputText.SpecialMarker af|inputText::label {font-weight: bold}
Advertisements

Deploying your application to a reserved port number

Posted on Updated on

Ensure that you avoid reserved port numbers when deploying your application. Some application servers will fail to prevent you from deploying your application on a reserved server port (e.g. 1, 7, 9, 11, 13, 15, 17, 19, 20, 21, 22, 23, 25, 37, 42, 43, 53, 77, 79, 87, 95, 101, 102, 103, 104, 109, 110, 111, 113, 115, 117, 119, 123, 135, 139, 143, 179, 389, 465, 512, 513, 514, 515, 526, 530, 531, 532, 540, 556, 563, 587, 601, 636, 993, 995, 2049, 3659, 4045, 6000, 6665, 6666, 6667, 6668, 6669). If you use one of these reserved ports, some web browsers may honor it but at least WebKit-based browsers (e.g. Safari and Google Chrome) will not. You may see some page content but all resources (images and styles) will fail to load.

Custom Java Bind Variable in a Where Clause of an ADF View Object

Posted on Updated on

You can make a JDeveloper model View Object (VO) use a data query that has a custom variable accessor in Java.  This is useful for cases where you want your where clause to select based on the current user name from the ADF Security context.

Go to the “Query” tab of your view object:


Add a new Bind Variable and make sure to specify “Expression” and give the value the Groovy expression “adf.object.viewObject.yourPropertyName”.  The “adf.object.viewObject” portion is essentially like saying “this object” and then it will call getYourPropertyName() to fetch the value:


Back in your query expression, refer to this new bind variable by its name but prepend “:” to indicate that it is a bind variable as shown above.

Select the “Java” tab of your view object and then click the pencil icon to edit the settings:


Select the “Generate View Object Class” and “Include bind variable accessors” options:


This will generate a new “.java” entry under your view object in the JDeveloper application navigator:


Finally, open that Java file and change the implementation of your get accessor as you wish.  In my case, I am simply returning the user name property from the ADF security context.  I also changed my set accessor to do nothing:

Decrypting Margin, Border, and Padding Styles

Posted on Updated on

When working with ADF Faces skins, JSF inlineStyles, or just CSS in general, you may have come across some explicit style definitions like this which is very easy to understand:

padding-top: 15px;

However, you may have also come across more cryptic style definitions like these but wonder which measurement applies to which side of the element:

padding: 1px 2px 3px 4px;
padding: 5px 6px 7px;
padding: 8px 9px;
padding: 10px;

Here’s a key for decoding these definitions:

padding:   top   right   bottom   left;
padding:   top   right/left   bottom;
padding:   top/bottom   right/left;
padding:   top/bottom/right/left;

This same pattern applies to margins and borders.

Aside from the cryptic definitions, see my other post displaying how the CSS Box Model treats margins, borders, and padding with respect to width and height dimensions.  If you are an ADF Faces application developer be sure to also read the Layout Basics page.

Adding Skin and Settings menus to your JDeveloper ADF application

Posted on Updated on

Ever want to display menus in your Oracle JDeveloper 11g ADF Faces rich client application so users can easily change skins or other settings on the fly?  Perhaps you don’t want to expose these menus to your end users but at least want some easy way for you to test these different options while you develop or debug your application.  This post will show you how to get it done similar to what you see on the hosted ADF Faces Rich Client demos.

Screen shot of skin and settings menus
Example skin and settings menus

Before we begin with the changes, let’s start with some background:

Skins

A skin is an Apache MyFaces Trinidad construct that is also used in ADF Faces.  A skin is very much like a standard CSS style sheet that you use to define styles used by an HTML page.  Skins abstract implementation details in the same manner that components in JavaServer Faces (JSF) abstract you from underlying HTML implementation details.  The syntax of a skin follows that of the CSS 3 spec.  Skins can extend other skins, have blocks for user agent/browser-specific definitions, accessibility-specific definitions, and other properties that would normally not be available in a CSS file.

If you are interested in creating your own skin then I would recommend reviewing these pages:

Settings

The ADF Faces framework uses settings defined in the WEB-INF/trinidad-config.xml file of your application.  This file allows you to control settings such as:

  • Which skin used by your application
  • Whether a screen reader is used
  • Whether the page should be optimized for high contrast colors
  • Whether the page should be optimized for large fonts
  • Whether animation should be enabled in component interactions

See the configuration documentation for more details on this file.

Now here are the details for how to create these menus:

The Configuration File

If your application does not already have one, create a new WEB-INF/trinidad-config.xml file.  We want to make these settings dynamic so let’s use EL expressions for the values like this:

<?xml version="1.0" encoding="UTF-8"?>
<trinidad-config xmlns="http://myfaces.apache.org/trinidad/config">
 <skin-family>#{skins.skinFamily}</skin-family>
 <accessibility-mode>#{accessibility.accessibilityMode}</accessibility-mode>
 <accessibility-profile>#{accessibility.accessibilityProfile}</accessibility-profile>
 <animation-enabled>#{animation.animationEnabled}</animation-enabled>
</trinidad-config>

I am using separate managed beans here primarily for organization but you could just use a single bean.  The skin-family specifies what the active skin is.  The accessibility-mode specifies whether the page should be targeted for a screen reader.  The accessibility-profile specifies whether high contrast colors and whether large fonts should be targeted for the page.  The animation-enabled specifies whether the user will see animations in components that provide them.

The Menu Components

Some af:menuBar (most likely one that exists in your application’s page template) will need some af:menu components.  In this example, we have one menu that lists all of the skins that we want to expose and another menu that lists the settings.

ADF Faces comes with some built-in skins that have an inheritance structure like this:

  • simple — plain, nearly void of color, basic appearance
    • blafplus-medium — rectangular, solid blue colors
      • blafplus-rich — rounded, solid blue colors
    • fusion — blue colors with a rich three-dimensional appearance
      • fusion-projector — similar to the fusion skin but with colors suited for poor quality screen projectors

We will make our skin menu use radio menu items to display which skin is active:

 <af:menu text="#{bundle.MENU_SKIN}" id="skins">
   <af:commandMenuItem
     id="skin1"
     text="blafplus-rich"
     type="radio"
     actionListener="#{skins.skinMenuAction}"
     selected="#{skins.skinFamily=='blafplus-rich'}"/>
   <af:commandMenuItem
     id="skin2"
     text="blafplus-medium"
     type="radio"
     actionListener="#{skins.skinMenuAction}"
     selected="#{skins.skinFamily=='blafplus-medium'}"/>
   <af:commandMenuItem
     id="skin3"
     text="fusion"
     type="radio"
     actionListener="#{skins.skinMenuAction}"
     selected="#{skins.skinFamily=='fusion'}"/>
   <af:commandMenuItem
     id="skin4"
     text="fusion-projector"
     type="radio"
     actionListener="#{skins.skinMenuAction}"
     selected="#{skins.skinFamily=='fusion-projector'}"/>
   <af:commandMenuItem
     id="skin5"
     text="simple"
     type="radio"
     actionListener="#{skins.skinMenuAction}"
     selected="#{skins.skinFamily=='simple'}"/>
 </af:menu>

The settings menu items will be presented as individual check mark options.  I am using a group to separate the animation setting from the others that are more accessibility-related:

 <af:menu text="#{bundle.MENU_SETTINGS}" id="config">
   <af:group id="acc">
     <af:commandMenuItem
       id="accMode"
       text="#{bundle.ACCESSIBILITY_LABEL_SCREEN_READER}"
       shortDesc="#{bundle.ACCESSIBILITY_SHORT_DESC_SCREEN_READER}"
       selected="#{accessibility.screenReader}"
       type="check"
       actionListener="#{accessibility.modeMenuAction}"/>
     <af:commandMenuItem
       id="contrast"
       text="#{bundle.ACCESSIBILITY_LABEL_HIGH_CONTRAST}"
       shortDesc="#{bundle.ACCESSIBILITY_SHORT_DESC_HIGH_CONTRAST}"
       selected="#{accessibility.highContrast}"
       type="check"
       actionListener="#{accessibility.colorContrastMenuAction}"/>
     <af:commandMenuItem
       id="fonts"
       text="#{bundle.ACCESSIBILITY_LABEL_LARGE_FONTS}"
       shortDesc="#{bundle.ACCESSIBILITY_SHORT_DESC_LARGE_FONTS}"
       selected="#{accessibility.largeFonts}"
       type="check"
       actionListener="#{accessibility.fontSizeMenuAction}"/>
   </af:group>
   <af:commandMenuItem
     id="animate"
     text="#{bundle.MENU_ITEM_ENABLE_ANIMATIONS}"
     type="check"
     selected="#{animation.animationEnabled=='true'}"
     actionListener="#{animation.animationMenuAction}"/>
 </af:menu>

Managed Beans

We could have hard-coded the values we were after in the configuration file but then you’d have to restart your application when you make changes.  Since we want these settings to be dynamic, we use EL to point these values to managed beans.  These same beans are used to handle the menu item actions as well as convey their selected states.

The managed beans can be defined in the faces-config.xml like this (using session scope to preserve the settings for the span of the user’s session):

<!-- A managed bean that we use to track the user's skin preference. -->
<managed-bean>
  <managed-bean-name>skins</managed-bean-name>
  <managed-bean-class>foo.SkinPreferences</managed-bean-class>
  <managed-bean-scope>session</managed-bean-scope>
</managed-bean>

<!-- A managed bean that we use to track the user's accessibility preference. -->
<managed-bean>
  <managed-bean-name>accessibility</managed-bean-name>
  <managed-bean-class>foo.AccessibilityPreferences</managed-bean-class>
  <managed-bean-scope>session</managed-bean-scope>
</managed-bean>

<!-- A managed bean that we use to track the user's animation preference. -->
<managed-bean>
  <managed-bean-name>animation</managed-bean-name>
  <managed-bean-class>foo.AnimationPreferences</managed-bean-class>
  <managed-bean-scope>session</managed-bean-scope>
</managed-bean>

The session-scoped SkinPreferences managed bean defines your default skin and looks like this:

public class SkinPreferences implements Serializable
{
 public String getSkinFamily()
 {
   return _skinFamily;
 }

 public void setSkinFamily(String family)
 {
   _skinFamily = family;
 }

 public void skinMenuAction(ActionEvent ae)
 {
   RichCommandMenuItem menuItem = (RichCommandMenuItem)ae.getComponent();
   setSkinFamily(menuItem.getText());
   reloadThePage();
 }

 public static void reloadThePage()
 {
   // Since the page might be using PPR navigation,
   // we need to issue a redirect to ensure the entire page redraws:
   FacesContext fContext =
     FacesContext.getCurrentInstance();
   String viewId =
     fContext.getViewRoot().getViewId();
   String actionUrl =
     fContext.getApplication().getViewHandler().getActionURL(fContext, viewId);
   try
   {
     ExternalContext eContext = fContext.getExternalContext();
     String resourceUrl = actionUrl;
     eContext.redirect(resourceUrl);
   }
   catch (IOException ioe)
   {
     System.err.println("Problem trying to reload the page:");
     ioe.printStackTrace();
   }
 }

 private String _cachedTabBarHeight = null;
 private String _skinFamily = _DEFAULT_SKIN;
 private static final String _DEFAULT_SKIN = "fusion";
 private static final long serialVersionUID = 1L;
}

The session-scoped AccessibilityPreferences managed bean looks like this:

public class AccessibilityPreferences implements Serializable
{
  public String getAccessibilityMode()
  {
    return _screenReader ? "screenReader" : "default";
  }

  public AccessibilityProfile getAccessibilityProfile()
  {
    // Use safe double-check locking just in case we have multiple threads coming
    // through.
    if (_profile == null)
    {
      synchronized (this)
      {
        if (_profile == null)
          _profile = _createAccessibilityProfile();
      }
    }
    return _profile;
  }

  public boolean isScreenReader()
  {
    return _screenReader;
  }

  public boolean isHighContrast()
  {
    return _highContrast;
  }

  public boolean isLargeFonts()
  {
    return _largeFonts;
  }

  public void setScreenReader(boolean screenReader)
  {
    _screenReader = screenReader;
  }

  public synchronized void setHighContrast(boolean highContrast)
  {
    _highContrast = highContrast;

    // We need to re-create the AccessibilityProfile instance if any of the profile
    // properties change.
    // Null out our old cached copy.
    // It will be re-created the next time that getAccessibilityProfile() is called.
    _profile = null;
  }

  public synchronized void setLargeFonts(boolean largeFonts)
  {
    _largeFonts = largeFonts;

    // We need to re-create the AccessibilityProfile instance if any of the profile
    // properties change.
    // Null out our old cached copy.
    // It will be re-created the next time that getAccessibilityProfile() is called.
    _profile = null;
  }

  public void modeMenuAction(ActionEvent ae)
  {
    setScreenReader(!isScreenReader());
    SkinPreferences.reloadThePage();
  }

  public void colorContrastMenuAction(ActionEvent ae)
  {
    setHighContrast(!isHighContrast());
    SkinPreferences.reloadThePage();
  }

  public void fontSizeMenuAction(ActionEvent ae)
  {
    setLargeFonts(!isLargeFonts());
    SkinPreferences.reloadThePage();
  }

  // Creates the AccessibilityProfile instance based on the user's current
  // accessibility preferences.
  private AccessibilityProfile _createAccessibilityProfile()
  {
    AccessibilityProfile.ColorContrast colorContrast = isHighContrast() ?
      AccessibilityProfile.ColorContrast.HIGH :
      AccessibilityProfile.ColorContrast.STANDARD;

    AccessibilityProfile.FontSize fontSize = isLargeFonts() ?
      AccessibilityProfile.FontSize.LARGE :
      AccessibilityProfile.FontSize.MEDIUM;

    return AccessibilityProfile.getInstance(colorContrast, fontSize);
  }

  // We hold a reference to a cached copy of the AccessibilityProfile so that we do
  // not need to re-create this object each time getAccessibilityProfile() is
  // called.
  // Note that we mark this as volatile so that we can safely perform double-checked
  // locking on this variable.
  private volatile AccessibilityProfile _profile;

  private boolean _screenReader;
  private boolean _highContrast;
  private boolean _largeFonts;

  private static final long serialVersionUID = 1L;
}

The session-scoped AnimationPreferences managed bean looks like this:

public class AnimationPreferences implements Serializable
{
  public boolean isAnimationEnabled()
  {
    return _animationEnabled;
  }

  public void setAnimationEnabled(boolean enabled)
  {
    _animationEnabled = enabled;
  }

  public void animationMenuAction(ActionEvent ae)
  {
    setAnimationEnabled(!isAnimationEnabled());
    SkinPreferences.reloadThePage();
  }

  private boolean _animationEnabled = true;
  private static final long serialVersionUID = 1L;
}

Adding an Accessibility Page

If you wanted to take this a step further (and do not have built-in, predefined user-specific settings after a user sign in screen), you should provide an “Accessibility” link.  This link should be exposed as early as possible in your page so that screen reader users can easily identify that you are providing them options.  This page should then provide a reduced user interface for simple settings configuration.

Screen shot of the accessibility preferences page
The accessibility preferences page

Extra faces-config.xml changes:

<!--
  A managed bean that holds onto the user's accessibility preferences while
  editing these value on the Accessibility Preferences page.  Note that unlike
  the session-scoped AccessibilityPreferences bean, this bean only requires
  request-scope, since it is only holding the values for a short time (for
  the life of one request).
  -->
<managed-bean>
  <managed-bean-name>accessibilityHolder</managed-bean-name>
  <managed-bean-class>foo.AccessibilityPreferencesHolder</managed-bean-class>
  <managed-bean-scope>request</managed-bean-scope>
  <!--
    We use a managed-property to inject the AccessibilityPreferences
    instance into our holder.  This allows the holder both to determine
    its initial values as well as apply back any changes.
    -->
  <managed-property>
    <property-name>accessibilityPreferences</property-name>
    <value>#{accessibility}</value>
  </managed-property>
</managed-bean>

The accessibility page should contain content like this:

<af:panelHeader id="ph1" text="#{bundle.ACCESSIBILITY_TITLE}">
  <af:panelFormLayout id="pfl1">
    <f:facet name="footer">
      <af:panelGroupLayout id="pgl2" layout="horizontal">
        <f:facet name="separator">
          <af:spacer id="s1" width="4px"/>
        </f:facet>
        <af:commandButton
          text="#{bundle.OK}"
          id="ok"
          action="#{accessibilityHolder.ok}"/>
        <af:commandButton
          text="#{bundle.CANCEL}"
          id="cancel"
          action="#{accessibilityHolder.cancel}"/>
      </af:panelGroupLayout>
    </f:facet>
    <af:selectBooleanCheckbox
      value="#{accessibilityHolder.screenReader}"
      id="sbc1"
      text="#{bundle.ACCESSIBILITY_LABEL_SCREEN_READER}"
      shortDesc="#{bundle.ACCESSIBILITY_SHORT_DESC_SCREEN_READER}"/>
    <af:selectBooleanCheckbox
      value="#{accessibilityHolder.highContrast}"
      id="sbc2"
      text="#{bundle.ACCESSIBILITY_LABEL_HIGH_CONTRAST}"
      shortDesc="#{bundle.ACCESSIBILITY_SHORT_DESC_HIGH_CONTRAST}"/>
    <af:selectBooleanCheckbox
      value="#{accessibilityHolder.largeFonts}"
      id="sbc3"
      text="#{bundle.ACCESSIBILITY_LABEL_LARGE_FONTS}"
      shortDesc="#{bundle.ACCESSIBILITY_SHORT_DESC_LARGE_FONTS}"/>
  </af:panelFormLayout>
</af:panelHeader>

The request-scoped AccessibilityPreferencesHolder managed bean looks like this:

/**
 * AccessibilityPreferencesHolder is a request-scope managed bean that temporarily
 * holds on to accessibility preferences while the end user is editing them in the
 * accessibility preferences page.  While interacting with the accessibility page,
 * we avoid pushing values directly into the AccessibilityPreferences instance,
 * since it is possible that the end user may leave the page without accepting these
 * changes.
 *
 * The values stored in this temporary object are only pushed into the
 * AccessibilityPreferences instance when the user commits the changes by clicking
 * the "OK" button
 * on the accessibility preferences page.  When leaving the page via any other means
 * (Cancel button or other command components), any changes stored in the
 * AccessibilityPreferencesHolder are discarded.
 *
 * This object is exposed as a rqeuest-scope managed bean with the name
 * "accessibilityHolder" (see faces-config.xml).  It can be accessed via the EL
 * expression "#{accessibilityHolder}".
 */
public class AccessibilityPreferencesHolder
{
  /**
   * Tests whether the user prefers screen reader-optimized content.
   */
  public boolean isScreenReader()
  {
    return _screenReader;
  }

  /**
   * Tests whether the user prefers high contrast-optimized content.
   */
  public boolean isHighContrast()
  {
    return _highContrast;
  }

  /**
   * Tests whether the user prefers content optimized for large fonts.
   */
  public boolean isLargeFonts()
  {
    return _largeFonts;
  }

  /**
   * Stores the screen reader preference.
   */
  public void setScreenReader(boolean screenReader)
  {
    _screenReader = screenReader;
  }

  /**
   * Stores the high contrast preference.
   */
  public void setHighContrast(boolean highContrast)
  {
    _highContrast = highContrast;
  }

  /**
   * Stores the large fonts preference.
   */
  public void setLargeFonts(boolean largeFonts)
  {
    _largeFonts = largeFonts;
  }

  /**
   * Setter for injecting the AccessibilityPreferences managed-property.
   * The managed-bean definition specifies that the AccessibilityPreferences
   * instance should be injected into this object via the
   * setAccessibilityPreferences() method. (See faces-config.xml.)
   */
  public void setAccessibilityPreferences(AccessibilityPreferences preferences)
  {
    // Use the session-scoped AccessibilityPreferences to initialize our
    // values to the user's current preferences.
    _largeFonts = preferences.isLargeFonts();
    _highContrast = preferences.isHighContrast();
    _screenReader = preferences.isScreenReader();

    // Keep a reference to the AccessibilityPreferences instance.  We'll need
    // access to this object in order to commit the user's new preferences.
    _preferences = preferences;
  }

  /**
   * Handles OK button clicks.
   */
  public String ok()
  {
    // Store the user's new preferences, making them active.
    _preferences.setScreenReader(isScreenReader());
    _preferences.setHighContrast(isHighContrast());
    _preferences.setLargeFonts(isLargeFonts());

    // Specify where to navigate to next.
    return "home"; // change this for your app's needs
  }

  /**
   * Handles cancel button clicks.
   */
  public String cancel()
  {
    // Don't save any new values - just return.
    return "home"; // change this for your app's needs
  }

  // User's current preferences.
  private boolean _largeFonts;
  private boolean _highContrast;
  private boolean _screenReader;

  // Injected AccessbilityPreferences instance.
  private AccessibilityPreferences _preferences;
}

What’s New in JSF 2?

Posted on Updated on

Andy Schwartz has created a fantastic introduction to the new features of JavaServer Faces 2:

Stretchable Tiled Layout

Posted on Updated on

With the Oracle ADF Faces Rich Client, have you ever wanted to divide a stretchable box into tiles?

Divided in Half

Let’s say you just wanted to divide a stretchable area in half, the top half gets 50% of the space and the bottom half gets the other 50%.  If your stretchable area is one created by a component that stretches its children (please read Layout Basics if this term doesn’t mean anything to you), you achieve this half-and-half layout by using a panelStretchLayout:

<af:panelStretchLayout id="half" topHeight="50%"
  dimensionsFrom="parent">
  <f:facet name="top">
    <!-- top half component rooted here -->
  </f:facet>
  <f:facet name="center">
    <!-- bottom half component rooted here
         (the center facet gets the remaining space) -->
  </f:facet>
</af:panelStretchLayout>

Remember, avoid inlineStyle if at all possible; do you see any inlineStyle values here?

Divided into Thirds

What if you wanted it to be divided into thirds?  Just add one more facet to the panelStretchLayout like this:

<af:panelStretchLayout id="thirds" topHeight="33%" bottomHeight="33%"
  dimensionsFrom="parent">
  <f:facet name="top">
    <!-- top third component rooted here -->
  </f:facet>
  <f:facet name="center">
    <!-- middle third component rooted here
         (the center facet gets the remaining space) -->
  </f:facet>
  <f:facet name="bottom">
    <!-- bottom third component rooted here -->
  </f:facet>
</af:panelStretchLayout>

Divided into Quarters

Okay so now what are you gonna do–the panelStretchLayout doesn’t have any more facets to choose from that would appear below “bottom”?!

Well, now you need to nest panelStretchLayouts where the inner panelStretchLayout uses 3 facets (space divided into thirds) and an outer panelStretchLayout that uses 2 facets (space divided into 25% and 75%) like this:

<af:panelStretchLayout id="outer" topHeight="25%"
  dimensionsFrom="parent">
  <f:facet name="top">
    <!-- first quarter component rooted here -->
  </f:facet>
  <f:facet name="center">
    <af:panelStretchLayout id="inner" topHeight="33%" bottomHeight="33%"
      dimensionsFrom="parent">
      <f:facet name="top">
        <!-- second quarter component rooted here -->
      </f:facet>
      <f:facet name="center">
        <!-- third quarter component rooted here
             (the center facet gets the remaining space) -->
      </f:facet>
      <f:facet name="bottom">
        <!-- fourth quarter component rooted here -->
      </f:facet>
    </af:panelStretchLayout>
  </f:facet>
</af:panelStretchLayout>

These all line up vertically, what if I wanted horizontal tiles?

Instead of using “top” and “bottom” facets, use “start” and “end”.

What if I don’t have a stretchable area?

Without being stretchable, you cannot evenly distribute space.  You would then use something like an Apache MyFaces Trinidad trh:tableLayout (as seen in the Tiled Flowing demo or even a layout=”vertical” af:panelGroupLayout.

Are there any other tiled layout solutions?

Yes, for the stretchable area case, you could:

  • Use panelSplitters instead of panelStretchLayouts though splitter panels don’t use percentage units–only pixels, or
  • Use panelGridLayout (don’t forget halign=”stretch” and valign=”stretch” on your gridCell components).
  • Use grid structure to generate the tiles as seen in the Tiled Stretching demo.

For more info, refer to the demos, documentation, forums, news, etc. on the Oracle ADF Faces Rich Client Components page on the Oracle Technology Network: