Customizing activity rendering
Explains how to customize the visual representation of one or more activities.
Describes what an activity renderer is and explains the associations between the classes used for rendering.
Describes the main activity renderers and the differences between
IlvActivityGraphic and
IlvGraphic.
Explains how to combine default and customized activity renderers using the Custom Gantt Chart sample.
Explains how to render activities that include several intermediary dates.
Explains how activity rendering can be customized with the API.
Explains how to assign a renderer to all activities using factory classes.
The Activity Rendering API
An activity renderer is an object that contributes to the visual representation of one or more activities. In the simplest case, an activity renderer is used to draw everything related to an activity. Renderers can also be combined. For example, one renderer is used to draw a bar, another draws a label, and two more draw the markers at the start and end of the activity.
You can apply activity renderers to many activities at once. An activity renderer can store references to the colors, fonts and the dimensions that apply to all corresponding activities. However, normally it does not store the size or label attributes that apply to a single activity. If it does, such information is highly transient and must be recomputed each time the activity renderer is accessed on behalf of the particular activity.
The two main activity rendering classes are:
The following figure shows the associations between rendering classes.
Simple activity renderers
Simple activity renderers draw a single, simple graphic representation. All simple activity renderers are subclasses of IlvActivityGraphicRenderer. An instance of IlvActivityGraphicRenderer uses a single IlvGraphic object as a delegate for drawing.
The differences between the
IlvActivityGraphic and
IlvGraphic are:
An
IlvActivityGraphic instance delegates rendering tasks to its renderer. The simple parts of a renderer delegate to their respective
IlvGraphic instances.
An
IlvActivityGraphic instance is part of a Gantt sheet. Calls to
IlvActivityGraphic.getGraphicBag() return an
IlvGanttRow instance. In turn, calls to
IlvGanttRow.getGraphicBag() return an
IlvGanttSheet object. Calls to
getGraphicBag() from the graphic delegate of a renderer, on the other hand, return
null.
An
IlvActivityGraphic object always represents an entire activity. The graphic delegate of a renderer contained in an
IlvActivityCompositeRenderer instance is responsible for a part of the drawing of the activity.
The following table shows the most important classes of simple activity renderers.
Class | Description |
| Draws a bar. Optionally, a label is added to the bar. This label is always clipped to the bounds of the bar. |
| Draws a label. You use this class when you need an unclipped label. |
| Draws a symbol at the start or end of an IlvActivityBar. |
Combining activity renderers
You combine activity renderers with the
IlvActivityCompositeRenderer class.
An IlvActivityCompositeRenderer instance holds a number of child renderers and delegates rendering tasks to them. Child renderers with a higher index are drawn after renderers with a lower index, and appear to be placed above them in the Gantt chart.
An IlvActivityCompositeRenderer instance has a fixed number of child renderers. While the renderer is in use, every child renderer is assigned a fixed index. You can remove a child renderer by storing null at its index. However, it is not possible to reduce the number of available indices.
For example, suppose an IlvActivityCompositeRenderer instance has the following child renderers:
An
IlvActivityBar instance that represents the completion bar, This bar is drawn on top of the main bar.
An
IlvActivityLabel instance that represents a label drawn to the right of the main bar.
An
IlvActivitySymbol instance that represents the marker displayed at the start of the activity.
An
IlvActivitySymbol instance that represents the marker displayed at the end of the activity.
The following information is drawn from the Rendering a Custom Data Model sample located in:
The Custom Gantt Chart example represents an
IlvGeneralActivity instance in a data model with customized rendering:
Leaf activities are rendered as a composite of the default
IlvActivityBar class. This object displays the name of the activity with a thin yellow activity bar. The following figure displays an activity bar that is proportional to the priority level of the activity.
The following figure shows the rendering of CustomActivity instances.
Some activities show a black completion bar below the main bar. The completion value is taken as a property from the activity. The
<installdir>/jviews-gantt/samples/customData/src/customData/CompletionProperty.java class provides centralized access to this property. The static methods
getCompletion and
setCompletion are used to access an activity while providing a range check and a default value:
public static Number getCompletion(IlvActivity activity) {
if (!(activity instanceof IlvUserPropertyHolder))
return null;
Number completion =
(Number)((IlvUserPropertyHolder) activity).getProperty(COMPLETION_PROPERTY);
if (completion == null)
completion = new Integer(0);
return completion;
}
public static void setCompletion(IlvActivity activity, Number completion) {
if (!(activity instanceof IlvUserPropertyHolder))
throw new IllegalArgumentException("Completion cannot be set on " + activity);
if (completion == null)
throw new IllegalArgumentException("Completion cannot be null");
if (completion.doubleValue() < 0)
completion = new Integer(0);
((IlvUserPropertyHolder)activity).setProperty(COMPLETION_PROPERTY, completion);
}
It is also useful to create a derived,
String valued property class. The class
<installdir>/jviews-gantt/samples/customData/src/customData/FormattedCompletionProperty.java is a full implementation of the
IlvStringProperty interface. In the Rendering a Custom Data Model sample, you use a
FormattedCompletionProperty instance to display completion values as text in the table cells or as label on the activity renderers.
FormattedCompletionProperty is a subclass of
IlvFormattedNumberProperty and thus inherits its numerical formatting capabilities:
public class FormattedCompletionProperty extends IlvFormattedNumberProperty {
...
// Returns the completion proportion of the specified activity.
protected Object getValueImpl(Object activity) {
return CompletionProperty.getCompletion((IlvActivity)activity);
}
// Sets the completion proportion of the specified activity.
protected void setValueImpl(Object activity, Object completion) {
CompletionProperty.setCompletion((IlvActivity)activity, (Number)completion);
}
...
}
The black bar below the main bar is a
<installdir>/jviews-gantt/samples/customData/src/customData/CompletionBar.java object.
CompletionBar is a subclass of
IlvActivityBar. In the following code example, the displayed property is set to
null so no text is rendered.
No Text Rendered
class CompletionBar extends IlvActivityBar {
public CompletionBar() {
setDisplayedProperty(null);
}
}
The length of the CompletionBar object is rendered proportionally to the completion percentage and the duration of the activity. This is done by overriding the getEndTime method. The getStartTime method remains unchanged. The isRelayoutNeeded method is overridden so that each activity is redrawn automatically whenever its completion value changes. The following code sample shows these changes.
Rendering Length Proportionally and Redrawing Automatically
// Returns the time point that defines the end of this bar.
public Date getEndTime(IlvActivityGraphic ag) {
Date startTime = getStartTime(ag);
IlvActivity activity = ag.getActivity();
Number completion;
completion = CompletionProperty.getCompletion(activity);
if (completion != null) {
return IlvTimeUtil.subtract(super.getEndTime(ag), startTime)
.multiply(completion.doubleValue())
.add(startTime);
} else {
return startTime;
}
}
// Determines if the bounding box may have changed.
public boolean isRelayoutNeeded(ActivityEvent evt) {
return super.isRelayoutNeeded(evt) || CompletionProperty.getInstance().isPropertyChangedEvent((EventObject)evt);
}
The CompletionBar.getToolTipText method has been overridden so that when the mouse hovers over the completion bar, the completion value of the activity is displayed.
Displaying tooltips
public String getToolTipText(IlvActivityGraphic ag,
IlvPoint p,
IlvTransformer t) {
IlvActivity activity = ag.getActivity();
Number completion = CompletionProperty.getCompletion(activity);
if (completion != null)
return MessageFormat.format("{0}% completed",
Math.round(completion.doubleValue()*100));
else
return null;
}
In the Rendering a Custom Data Model sample, the background color of a completion bar in a composite renderer is set to black. Vertical margins are added to avoid overlapping with the other bars. The following code sample shows the corresponding part of the CustomActivityRenderer class.
Custom Activity Renderer Class
private void initializeCompletionBar() {
...
completionBar = new CompletionBar();
completionBar.setTopMargin(0.5f);
completionBar.setBottomMargin(0.25f);
completionBar.setBackground(Color.black);
addRenderer(completionBar);
}
Rendering Activities with Multiple Dates
Activities which implement the
IlvPropertyHolderActivity interface, including the
IlvGeneralActivity and the
IlvTableActivity classes contain several intermediary dates, rather than only the start and end dates.
This can be used for the following purposes:
Represent the actual and planned extent of activities in the same Gantt chart, using a single model object for each activity.
Split activities and breaks. These activities are rendered as several bars with time intervals between two successive bars.
Display activities with embedded milestones, such as progress validations. No separate milestone objects are needed in the model.
Display lag time, that is, preparation work prior to the start, and cool down phases for the activity or termination work after the activity is completed can be displayed in the Gantt chart. No separate model objects are needed to display lag time.
The start and end of an activity are computed as the minimum and maximum of the date properties returned by calling IlvPropertyHolderActivity.getTimeProperties(). Date properties other than those returned by this method can also be used in the rendering, for example, the starting point of the lag time in the use case above.
Activities with multiple dates are rendered using an
IlvActivityCompositeRenderer, see
Combining activity renderers for more information. The child renderers of this
IlvActivityCompositeRenderer instance are simple renderers on which the start and end time properties have been set.
To represent two bars, one going from the START1 to the END1 dates, and the other from START2 to the END2 date:
1. Instantiate two IlvActivityBar instances.
2. Set the startTimeProperty and endTimeProperty of the first IlvActivityBar to START1 and END1
3. Set the same properties on the second IlvActivityBar to START2 and END2.
NOTE To display an IlvActivitySymbol for a particular date, set the startTimeProperty and endTimeProperty to the same date, for example END1.
The Custom Gantt Chart example demonstrates how to split activities into bars according to a holiday schedule. For the full code, see:
As part of the model creation, properties indicating the start and end time of each bar are set in the activities. For simplicity, the holiday schedule is assumed to consist only of weekends. As there are a lot of weekends that can interrupting an activity, many properties are needed. These properties are defined on the fly by calling CustomActivityRendererFactory.getSplitTimeProperty:
/**
* This property indicates the number of bars in a split activity.
*/
public static final String SPLIT_COUNT_PROPERTY = "Split";
/**
* Returns the name of the property that contains the start/end time of the
* i-th part of a split activity.
* @param i A nonnegative integer.
* @param endp true for the end time, false for the start time
*/
public static String getSplitTimeProperty(int i, boolean endp) {
return ((endp ? "E" : "S") + i).intern();
}
The following code example shows how to split the activity time interval. It is taken from:
ArrayList <String> = new ArrayList<String>();
int interval;
for (interval = 0;; interval++) {
...
String prop1 = CustomActivityRendererFactory.getSplitTimeProperty(interval, false);
String prop2 = CustomActivityRendererFactory.getSplitTimeProperty(interval, true);
activity.setProperty(prop1, t1);
activity.setProperty(prop2, t2);
timeProperties.add(prop1);
timeProperties.add(prop2);
...
}
if (interval > 1) {
activity.setTimeProperties(timeProperties.toArray(new String[timeProperties.size()]));
activity.setProperty(CustomActivityRendererFactory.SPLIT_COUNT_PROPERTY, new Integer(interval));
}
As shown in:
the renderer takes the pairs of time property values and creates a bar renderer for each pair:
public CustomActivityRenderer(IlvActivity activity) {
Integer splitCount =
(Integer) ((IlvGeneralActivity)activity).getProperty(
CustomActivityRendererFactory.SPLIT_COUNT_PROPERTY);
int n = splitCount.intValue();
for (int i = 0; i < n; i++) {
addRenderer(CustomActivityRendererFactory.getSplitRenderer(i));
}
initializeExtraChildRenderers();
}
The CustomActivityRendererFactory.getSplitRenderer method sets the start and end time properties of the bar renderer for a specific part of a split activity. Without these calls to setStartTimeProperty and setEndTimeProperty, all bars would extend from the start to the end of the entire activity and overlap.
IlvActivityGraphicRenderer barRenderer = (j == 0 ? new IlvActivityBar() : new IlvBasicActivtyBar());
barRenderer.setStartTimeProperty(getSplitTimeProperty(j, false));
barRenderer.setEndTimeProperty(getSplitTimeProperty(j, true));
Using Composite Graphics
The preferred way of using composite graphics is through the Designer. Composite graphics are not designed to be used programmatically, but the Rendering activities with composite graphics example shows how activity rendering can be customized using the API. The example code can be found at:
The following figure shows that in this sample:
Parent activities are rendered with composite graphics composed of bars with a color gradient and rectangles as end markers.
Leaf activities are rendered with composite graphics to display a bar with a color gradient, the name of the activity as text, and a square as decoration positioned near the left end of the bar.
The following figure shows the composite graphics for parent and leaf activity bars.
Composite Graphics
In the Rendering activities with composite graphics example found at
<installdir>/jviews-gantt/codefragments/application/compositeGraphic/src/CGORendererExample.java, the class
CustomParentActivityCompositeGraphic defines a customized graphic for parent activities.
These objects are composed of:
A rectangle with a color gradient.
Rectangles as end markers.
The method IlvGraphic.moveResize is overridden to change the graphic dynamically depending on its size.
The following code sample shows the definition of the parent-activity composite graphic.
Composite Graphic for a Parent Activity
class CustomParentActivityCompositeGraphic extends IlvCompositeGraphic
{
// The rectangle.
private IlvGeneralPath base;
// Decoration 1.
private IlvRectangle sRect;
// Decoration 2.
private IlvRectangle eRect;
...
// Overrides to update graphics depending on the composite's size changes.
public void moveResize(IlvRect size) {
super.moveResize(size);
final int decorationWidth = 5;
int height = (int) (size.height - 5);
rect.resize(size.width, height);
sRect.resize(decorationWidth, size.height);
eRect.resize(decorationWidth, size.height);
}
}
The class
CustomLeafActivityCompositeGraphic implementation found in
<installdir>/jviews-gantt/codefragments/application/compositeGraphic/src/CGORendererExample.java defines a customized graphic for child activities.
These objects are composed of:
A rectangle with a color gradient that represents the activity bar.
An
IlvText object that displays the name of the activity.
A square decoration.
The method CustomLeafActivityCompositeGraphic.setLabel changes the label display, externally through the renderer.
The method IlvGraphic.moveResize is overridden to change the graphic dynamically depending on its size.
The following code sample shows the definition of the leaf-activity composite graphic.
Composite Graphic for a Leaf Activity
class CustomLeafActivityCompositeGraphic extends IlvCompositeGraphic {
// The text graphic used to display the label.
private IlvText text;
// A decoration.
private IlvRectangle rect;
...
// Set a label to this graphic.
// @param label The label to display.
public void setLabel(String label) {
text.setLabel(label);
}
// Overrides to update graphics depending on the composite's size changes.
public void moveResize(IlvRect size) {
if (size.width > 0) {
super.moveResize(size);
...
}
}
}
Composite Graphic Renderers
The following figure shows the classes used to render composite graphics.
In the Rendering activities with composite graphics example found in
<installdir>/jviews-gantt/codefragments/application/compositeGraphic/src/CGORendererExample.java, the renderer classes
CustomParentActivityCompositeGraphicRenderer and
CustomLeafActivityCompositeGraphicRenderer use the definitions of the composite graphics to render parent and leaf activity graphics. They are subclasses of
IlvActivityCompositeGraphicRenderer. These classes are used to customize the composite graphic with which they are associated.
NOTE By default, an associated empty composite graphic is created.
The following code sample shows the definition of CustomParentActivityCompositeGraphicRenderer.
Definition of CustomParentActivityCompositeGraphicRenderer
class CustomParentActivityCompositeGraphicRenderer extends
IlvActivityCompositeGraphicRenderer {
// Builds a CustomParentActivityCompositeGraphicRenderer
public CustomParentActivityCompositeGraphicRenderer() {
this.setGraphic(new CustomParentActivityCompositeGraphic());
}
...
}
The class CustomLeafActivityCompositeGraphicRenderer overrides the method IlvActivityGraphicRenderer.prepareGraphic to update the label that is displayed and that depends on the activity name. The class CustomLeafActivityCompositeGraphicRenderer also overrides the method IlvActivityGraphicRenderer.isRedrawNeeded to trigger graphical updates on changes in activity name.
The following code sample shows the definition of CustomLeafActivityCompositeGraphicRenderer.
Definition of CustomLeafActivityCompositeGraphicRenderer
class CustomLeafActivityCompositeGraphicRenderer extends
IlvActivityCompositeGraphicRenderer {
// Builds a CustomLeafActivityCompositeGraphicRenderer
public CustomLeafActivityCompositeGraphicRenderer() {
this.setGraphic(new CustomLeafActivityCompositeGraphic());
}
// Overrides to update the label depending on the activity name.
// @param ag The activity graphic.
// @param t The current transformer.
protected IlvGraphic prepareGraphic(IlvActivityGraphic ag, IlvTransformer t)
{
CustomLeafActivityCompositeGraphic graphic =
(CustomLeafActivityCompositeGraphic) super.prepareGraphic(ag, t);
graphic.setLabel(ag.getActivity().getName());
return graphic;
}
// Overrides to trigger a redraw when the name is updated.
public boolean isRedrawNeeded(ActivityEvent evt) {
if (super.isRedrawNeeded(evt)) {
return true;
}
if (evt instanceof ActivityNameEvent) {
return true;
}
return false;
}
...
}
Renderer Factory for Composite Graphics
To customize activity rendering, you must create a customized activity renderer factory that creates the correct renderer on request. For more information, see
Combining activity renderers. The factory that creates activity renderers in the Composite Graphics code example is the class
CustomActivityCompositeGraphicRendererFactory.
The following code sample shows the skeleton of this factory.
Factory for Customizing Activity Renderers
public class CustomActivityCompositeGraphicRendererFactory extends
IlvDefaultActivityRendererFactory {
// Creates a customized activity renderer factory.
public CustomActivityCompositeGraphicRendererFactory(IlvHierarchyChart chart) {
super(chart);
// Customizes the leaf activity rendering.
setLeafActivityRenderer(new CustomLeafActivityCompositeGraphicRenderer());
// Customizes the parent activity rendering.
setParentActivityRenderer(new CustomParentActivityCompositeGraphicRenderer());
}
}
Installing Custom Activity Renderers
Activity renderers are installed by an instance of
IlvActivityRendererFactory. An
IlvActivityRendererFactory instance assigns a renderer to every activity; the same renderer instance is reused for many activities while treating special kinds activities simultaneously as needed.
The
IlvDefaultActivityRendererFactory distinguishes activities where the start date and the end date coincide, that is, leaf activities, parent activities, and milestones. An
IlvDefaultActivityRendererFactory instance uses a single renderer for each of the three categories.
The Rendering a Custom Data Model sample found in located in
<installdir>/jviews-gantt/samples/customData uses the new activity renderer factory by calling its
setActivityRendererFactory method.
The factory that creates activity renderers for the Rendering a Custom Data Model sample is an inner class of CustomGanttExample called CustomActivityRendererFactory. This class is an extension of the IlvDefaultActivityRendererFactory class. As such, it inherits a default renderer for parent activities.
The following code sample shows the skeleton of the factory.
Factory for Creating Custom Activity Renderers
public class CustomGanttExample extends GanttExample {
...
class CustomActivityRendererFactory
extends IlvDefaultActivityRendererFactory {
// Creates a customized activity renderer factory.
public CustomActivityRendererFactory(IlvHierarchyChart chart) {
super(chart);
// The leaf renderer is a composite renderer that will contain the 2
//bars.
setLeafActivityRenderer(new CustomActivityRenderer());
}
}
}
The leaf activity rendering is overridden by the call to IlvDefaultActivityRendererFactory.setLeafActivityRenderer made in the constructor.
You need to inform the Custom Gantt Chart example to use the new factory before any activities are initially rendered. This is done in the customizeFactories method because the chart has been created, but the data model has not yet been populated with activities:
Use the New Factory Before Rendering Activities
protected void customizeFactories() {
super.customizeFactories();
...
// Change the default activity renderer factory.
gantt.setActivityRendererFactory(new CustomActivityRendererFactory());
}
Copyright © 2018, Rogue Wave Software, Inc. All Rights Reserved.