Monday, November 16, 2009

Rounded Fading Galore

The look of some modern phones is wasteful in screen real-estate but stunning in visual effect. The rounded UI and faded scrolling are are gorgeous effects on newer devices that make a huge difference with very little work.

These effect can be added to LWUIT easily without overhauling anything. I neglected to add padding to the softbutton area in the demo code but other than that the code is trivial. I used a glass pane for the rounded border effect, you can use hardcoded image files to provide proper anti-aliasing.
The fade effect required gradient image masks and replacing the look and feel which in itself wasn't too difficult either.

BTW I very much enjoyed reading Angelo's post on gradients, this is something that our UI designer has been riding on me to get for a very long time... I'm still trying to figure out a way to integrate this into the whole designer/style paradigm in an easy to use way...

/**
* Allows overriding the form to give it a "rounded border" look and feel
* with a fade out in the bottom in case of scrolling.
*
* @author Shai Almog
*/

public class RoundFadeGlassPane implements Painter {
private Image topLeft;
private Image topRight;
private Image bottomLeft;
private Image bottomRight;
private Form parentForm;

public RoundFadeGlassPane(Form f) {
parentForm = f;
Image blackCorner = Image.createImage(60, 60);

Graphics cornersGraphics = blackCorner.getGraphics();
cornersGraphics.setColor(0);
cornersGraphics.fillRect(0, 0, blackCorner.getWidth(), blackCorner.getHeight());
cornersGraphics.setColor(0xffffff);
cornersGraphics.fillRoundRect(0, 0, 200, 200, 60, 60);

// get the white color since it might be modified when rendering and might not actually be 0xffffff
// e.g. it might be 0xfefefe due to rendering issues
int white = blackCorner.getRGBCached()[blackCorner.getHeight() / 2 * blackCorner.getWidth()];

// remove the white color from the image so only the black corners remain
blackCorner = blackCorner.modifyAlpha((byte)0xff, white);

topLeft = blackCorner;
topRight = topLeft.rotate(90);
bottomRight = topLeft.rotate(180);
bottomLeft = topLeft.rotate(270);
PainterChain.installGlassPane(f, this);
}

public void paint(Graphics g, Rectangle rect) {
g.drawImage(topLeft, 0, 0);
g.drawImage(topRight, parentForm.getWidth() - topRight.getWidth(), 0);
g.drawImage(bottomLeft, 0, parentForm.getHeight() - bottomRight.getHeight());
g.drawImage(bottomRight, parentForm.getWidth() - topRight.getWidth(), parentForm.getHeight() - bottomRight.getHeight());
}
}

/**
* Simple look and feel automatically installing the "round look" for applications
*
* @author Shai Almog
*/

public class RoundFadeLookAndFeel extends DefaultLookAndFeel {
private Object topMask;
private Object bottomMask;
private Image topCache;
private Image bottomCache;

public void bind(Component c) {
if(c instanceof Form) {
new RoundFadeGlassPane((Form)c);
}
}

private Object createFadeMask(boolean direction) {
Image mask = Image.createImage(Display.getInstance().getDisplayWidth(), Display.getInstance().getDisplayHeight() / 10);
Graphics g = mask.getGraphics();
g.setColor(0xffffff);
g.fillRect(0, 0, mask.getWidth(), mask.getHeight());
if(direction) {
g.fillLinearGradient(0, 0xffffff, 0, -5,
mask.getWidth(), mask.getHeight(), false);
} else {
g.fillLinearGradient(0xffffff, 0, 0, 5,
mask.getWidth(), mask.getHeight(), false);
}
return mask.createMask();
}

private Object getTopMask() {
if(topCache == null || topCache.getWidth() !=
Display
.getInstance().getDisplayWidth()) {
topMask = createFadeMask(false);
topCache = Image.createImage(
Display.getInstance().getDisplayWidth(),
Display.getInstance().getDisplayHeight() / 10);
}
return topMask;
}

private Object getBottomMask() {
if(bottomCache == null || bottomCache.getWidth() !=
Display.getInstance().getDisplayWidth()) {
bottomMask = createFadeMask(true);
bottomCache = Image.createImage(
Display.getInstance().getDisplayWidth(),
Display.getInstance().getDisplayHeight() / 10);
}
return bottomMask;
}

public void drawVerticalScroll(Graphics g, Component c,
float offsetRatio, float blockSizeRatio) {
if(offsetRatio + blockSizeRatio < 0.995) {
// we need to draw the fade on the bottom
Object bottom = getBottomMask();
Graphics temp = bottomCache.getGraphics();
temp.translate(0, bottomCache.getHeight() - c.getHeight());
c.paintBackgrounds(temp);
g.drawImage(bottomCache.applyMask(bottom), c.getX(), c.getY() + c.getHeight() - bottomCache.getHeight());
}
if(offsetRatio > 0) {
// we need to draw the fade on the top
Object top = getTopMask();
Graphics temp = topCache.getGraphics();
temp.translate(0, topCache.getHeight() - c.getHeight());
c.paintBackgrounds(temp);
g.drawImage(topCache.applyMask(top), c.getX(), c.getY());
}
super.drawVerticalScroll(g, c, offsetRatio, blockSizeRatio);
}
}

0 comments:

Post a Comment