Extending the J2ME Polish GUI with TextEffects

Text effects provide you with an easy way to spice up your fonts without caring about sizes and availability of characters in bitmap fonts. While there are various text-effects available, you might encounter a situation in which you want to create your own effect.

Concepts of TextEffect

A text effect takes over the rendering and layout of the text of a StringItem and all classes inheriting from StringItem. You can, however, apply your own effect by following these steps:

  1. Implement your effect by extending de.enough.polish.ui.TextEffect or an existing TextEffect subclass.
  2. Register the effect in ${polish.home}/custom-css-attributes.xml (optional).
  3. Apply your effect by specifying the text-effect CSS attribute in your project's polish.css file.

Implementing your TextEffect

Create your own effect by extending de.enough.polish.ui.TextEffect or an existing TextEffect subclass. For simple effects you only need to implement public drawString( String text, int textColor, int x, int y, int orientation, Graphics g ), which renders a single line of text at the specified position.
You can animate your effect by overriding one of the animate methods (see below).
If you want to design your text-effect using CSS, you need to implement the public void setStyle( Style style ) method, too.

Additionally, you can take over full control over the layout process by overriding or using the following methods:

  • public boolean animate(): Animates this effect - return true when you need a repaint.
  • public void animate(Item parent, long currentTime, ClippingRegion repaintRegion): Animates this effect - use this method if you need a repaint outside of the actual content area of the parent item.
  • public void drawStrings( String[] textLines, int textColor, int x, int y, int leftBorder, int rightBorder, int lineHeight, int maxWidth, int layout, Graphics g ): Use this method for drawing all rows of a StringItem at once - by default this method calls the above described drawString() method for each row.
  • public int getLeftX( int x, int orientation, int textWidth ): Calculates the horizontal start position - this is useful in case you need to draw RGB integer arrays, for example.
  • public int getTopY( int y, int orientation, Font font ), getTopY( int y, int orientation, int height, int baseLine ): Calculates the vertical top position.
  • public static int[] getRgbData( String text, int textColor, Font font ), public static int[] getRgbData(String text, int textColor, Font font, int x, int y, int width, int height ), public static int[] getRgbData(String text, int textColor, Font font, int x, int y, int width, int height, int transparentColor): Converts a font and a text into an RGB integer array on MIDP 2.0+ devices.
  • public void showNotify(): Notifies the effect that it is about to being shown.
  • public void hideNotify(): Notifies this effect that the corresponding item is to be hidden.
  • public void releaseResources(): Notifies this effect that it should release all resources like loaded images etc to free memory.
  • public int stringWidth(String str): Calculates the width that is required for the text.
  • public int getFontHeight(): Gets the height of the font / a single row of the text effect.
  • protected Font getFont(): Gets the used font.
  • public String[] wrap(String text, Font font, int firstLineWidth, int lineWidth): Wraps the text into several lines.

In the following example we explain the existing de.enough.polish.ui.texteffects.ShadowTextEffect that renders a shadow around a text:
the shadow text-effect in action

//#condition polish.usePolishGui
package de.enough.polish.ui.texteffects;

import javax.microedition.lcdui.Graphics;

import de.enough.polish.ui.Style;
import de.enough.polish.ui.TextEffect;

/**
 * Paints a shadow behind a text.
 * @author your name here
 */
public class MyShadowTextEffect 
extends TextEffect
{
	
	public static final int ORIENTATION_BOTTOM_RIGHT = 0;
	public static final int ORIENTATION_BOTTOM_LEFT = 1;
	public static final int ORIENTATION_TOP_RIGHT = 2;
	public static final int ORIENTATION_TOP_LEFT = 3;
	public static final int ORIENTATION_BOTTOM = 4;
	public static final int ORIENTATION_TOP = 5;
	public static final int ORIENTATION_RIGHT = 6;
	public static final int ORIENTATION_LEFT = 7;
	
	public int shadowColor;
	public int xOffset = 1;
	public int yOffset = 1;

	/**
	 * Creates a shadow effect.
	 */
	public MyShadowTextEffect() {
		super();
	}
	
	
	/* (non-Javadoc)
	 * @see de.enough.polish.ui.TextEffect#setStyle(de.enough.polish.ui.Style)
	 */
	public void setStyle(Style style) {
		super.setStyle(style);
		//#ifdef polish.css.text-myshadow-color
			Integer colorInt = style.getIntProperty("text-myshadow-color");
			if (colorInt != null) {
				this.shadowColor = colorInt.intValue();
			}
		//#endif
		//#ifdef polish.css.text-myshadow-orientation
			Integer orientationInt = style.getIntProperty("text-myshadow-orientation");
			if (orientationInt != null) {
				switch ( orientationInt.intValue() ) {
				case ORIENTATION_BOTTOM_RIGHT: 
					this.xOffset = 1;
					this.yOffset = 1;
					break;
				case ORIENTATION_BOTTOM_LEFT: 
					this.xOffset = -1;
					this.yOffset = 1;
					break;
				case ORIENTATION_TOP_RIGHT: 
					this.xOffset = 1;
					this.yOffset = -1;
					break;
				case ORIENTATION_TOP_LEFT: 
					this.xOffset = -1;
					this.yOffset = -1;
					break;
				case ORIENTATION_BOTTOM: 
					this.xOffset = 0;
					this.yOffset = 1;
					break;
				case ORIENTATION_TOP: 
					this.xOffset = 0;
					this.yOffset = -1;
					break;
				case ORIENTATION_RIGHT: 
					this.xOffset = 1;
					this.yOffset = 0;
					break;
				case ORIENTATION_LEFT: 
					this.xOffset = -1;
					this.yOffset = 0;
				}
			}
		//#endif
		//#ifdef polish.css.text-myshadow-x
			Integer xInt = style.getIntProperty("text-myshadow-x");
			if (xInt != null) {
				this.xOffset = xInt.intValue();
			}
		//#endif
		//#ifdef polish.css.text-myshadow-y
			Integer yInt = style.getIntProperty("text-myshadow-y");
			if (yInt != null) {
				this.yOffset = yInt.intValue();
			}
		//#endif
		//#debug
		System.out.println("ShadowTextEffect.setStyle(): color=" 
			+ Integer.toHexString(this.shadowColor) + ", x=" + this.xOffset + ", y=" + this.yOffset );
	}



	/* (non-Javadoc)
	 * @see de.enough.polish.ui.TextEffect#drawString(java.lang.String, int, int, int, int, javax.microedition.lcdui.Graphics)
	 */
	public void drawString(String text, int textColor, int x, int y, int orientation, Graphics g) 
	{
		g.setColor( this.shadowColor );
		g.drawString(text, x + this.xOffset, y + this.yOffset, orientation);
		g.setColor( textColor );
		g.drawString(text, x, y, orientation);
	}

}

Registering Your Text Effect

You can now register your implementation for easier usage in ${polish.home}/custom-css-attributes.xml:

<attribute name="text-effect">
	<mapping from="myshadow" 
	            to="new de.enough.polish.ui.texteffects.MyShadowTextEffect()" 
	/>
</attribute>

Since you are supporting some additional CSS attributes within the setStyle method, you need to register these attributes within ${polish.home}/custom-css-attributes.xml as well:

	<attribute
		name="text-myshadow-color"
		type="color"
		appliesTo="MyShadowTextEffect"
		description="Sets the color of a shadow for the shadow font-effect."
		default="black"
		id="89"
	/>
	
	<attribute
		name="text-myshadow-orientation"
		type="integer"
		appliesTo="MyShadowTextEffect"
		description="Sets the orientation of a shadow for the shadow font-effect."
		default="bottom-right"
		values="bottom-right, bottom-left, top-right, top-left, bottom, top, right, left"
		id="90"
	/>

	<attribute
		name="text-myshadow-x"
		type="integer"
		appliesTo="MyShadowTextEffect"
		description="Sets the x-orientation in pixels of a shadow for the shadow font-effect."
		default="1"
		id="91"
	/>
	
	<attribute
		name="text-myshadow-y"
		type="integer"
		appliesTo="MyShadowTextEffect"
		description="Sets the y-orientation in pixels of a shadow for the shadow font-effect."
		default="1"
		id="92"
	/>

Using Your Text Effect

You can use your effect for all items and screen elements that are based on StringItem, for example TextFields, screen titles, screen tickers, or item labels.

//#style myShadow
StringItem stringItem = new StringItem( "label: ", "hello world" );
form.append( stringItem );

Now use your effect by specifying it in the appropriate style of your polish.css file:

.myShadow {
	border-type: round-rect;
	border-color: gray;
	text-effect: myshadow;
}

In case you skipped the registration of your implementation in ${polish.home}/custom-css-attributes.xml you can also reference your implementation directly:

.myShadow {
	border-type: round-rect;
	border-color: gray;
	text-effect: new de.enough.polish.ui.texteffects.MyShadowTextEffect();
}

That's it - you've done it!

JavaDoc

TextEffect