有时候你也许要让你的MIDP应用程序显示一个计数器,比如某些操作从开始操作到现在进行了多少秒,这还算是一个简单的可以自动更新显示计数的定制组件,一些简单的代码便可使你让这些代码和Canvas完美整合,定制的绘制屏幕程序一般都继承自Cnavas, 或者用Form在MIDP2.0里的新组件---------CustomItem 。
制作一个双重目的的组件的关键是把它的描绘屏幕部分和控制部分分开,理由是没有一个概念能够让组件和Canvas作为一个整体起作用;应用程序必须本身能够绘制和控制整个Canvas,一个定制的Item,从某一方面说,是一个真实的组件,系统分派绘制屏幕的任务给这个组件,而对其他的大部分操作进行控制,你可以定义一个回调的接口让你的组件使用它们,并把这些控制任务交给合适的代码处理。
下面是有一个简单计数器例子的核心代码,CounterArea class:
import Javax.microedition.lcdui.*;
// The counter class, which can be used on a canvas
// or wrapped within a custom item.
public class CounterArea {
public static final int DEFAULT_RATE = 500;
public static final int MIN_RATE = 100;
// The callback interface by which we notify
// the counter owner of certain events.
public interface Callback {
void invalidateCounter( CounterArea counter );
void repaintCounter( CounterArea counter );
void resizeCounter( CounterArea counter );
}
public CounterArea(){
}
public CounterArea( int width, int height ){
_width = width;
_height = height;
}
public int getBackColor(){
return _backColor;
}
public Callback getCallback(){
return _callback;
}
public Font getFont(){
return _font;
}
public Font getFontForDrawing(){
return _font != null ? _font :
Font.getDefaultFont();
}
public int getHeight(){
if( _height < 0 ){
_height = getMinHeight();
}
return _height;
}
public int getMinHeight(){
return getFontForDrawing().getHeight();
}
public int getMinWidth(){
Font f = getFontForDrawing();
return f.stringWidth( Integer.toString( _value ) );
}
public int getRate(){
return _rate;
}
public int getTextColor(){
return _textColor;
}
public int getValue(){
return _value;
}
public int getWidth(){
if( _width < 0 ){
_width = getMinWidth();
}
return _width;
}
private void invalidate(){
if( _callback != null ){
_callback.invalidateCounter( this );
}
}
public boolean isCounting(){
return _timer != null;
}
public void paint( Graphics g ){
String s = Integer.toString( _value );
Font f = getFontForDrawing();
int w = f.stringWidth( s );
int h = f.getHeight();
int aw = getWidth();
int ah = getHeight();
g.setColor( _backColor );
g.fillRect( _left, _top, aw, ah );
g.setColor( _textColor );
g.drawString( s, _left + aw - w,
_top + ( ah - h ) / 2,
g.TOP g.LEFT );
if( w > aw h > ah ){
resize();
}
}
private void repaint(){
if( _callback != null ){
_callback.repaintCounter( this );
}
}
private void resize(){
if( _callback != null ){
_callback.resizeCounter( this );
}
}
private synchronized boolean increment( Runnable source ){
if( source != _timer ) return false;
++_value;
repaint();
return true;
}
public void setBackColor( int color ){
_backColor = color;
invalidate();
}
public void setCallback( Callback callback ){
_callback = callback;
}
public void setLeft( int left ){
_left = left;
}
public void setFont( Font f ){
_font = f;
invalidate();
}
public void setHeight( int h ){
_height = h;
}
public void setRate( int rate ){
_rate = ( rate < MIN_RATE ? MIN_RATE : rate );
}
public void setTextColor( int color ){
_textColor = color;
invalidate();
}
public void setTop( int top ){
_top = top;
}
public synchronized void setValue( int value ){
_value = value;
}
public void setWidth( int w ){
_width = w;
}
public synchronized void start(){
_timer = new CounterTimer();
new Thread( _timer ).start();
}
public synchronized void stop(){
_timer = null;
}
private int _backColor = 0x00FFFFFF;
private Callback _callback;
private Font _font;
private int _height = -1;
private int _index;
private int _left;
private int _rate = DEFAULT_RATE;
private int _textColor = 0x00000000;
private Runnable _timer;
private int _top;
private int _width = -1;
private int _value;
//-------------------------------------------------
// A very simple timer that sleeps and wakes
// up at regular intervals.
private class CounterTimer implements Runnable {
public void run(){
Thread t = Thread.currentThread();
while( true ){
try {
t.sleep( _rate );
}
catch( InterruptedException e ){
}
if( !increment( this ) ) break;
}
}
}
}
这个计数器在中垂线和横垂线的焦点绘制计数器数值,而且可以在绘制区域尺寸太小的情况下,自动更改合适的尺寸。它同时定义了一个公有的嵌套接口,CounterArea.Callback,将组件的无效性,重绘,自适应大小传递给适当的控制段,一个私有的内隐类,CounterTimer,负责在后台更新计数器的数值。
CounterArea类可以直接在一个Canvas中使用,但是你也可以把它和一个定制的Item封装在一起,外覆类,CounterItem,如下所示:
import javax.microedition.lcdui.*;
// A custom component for MIDP 2.0 that wraps
// a CounterArea instance.
public class CounterItem extends CustomItem
implements CounterArea.Callback {
public CounterItem(){
super( null );
_area = new CounterArea();
_area.setCallback( this );
}
public Font getFont(){
return _area.getFont();
}
public int getMinContentHeight(){
return _area.getMinHeight();
}
public int getMinContentWidth(){
return _area.getMinWidth();
}
public int getPrefContentHeight( int width ){
return getMinContentHeight();
}
public int getPrefContentWidth( int height ){
return getMinContentWidth();
}
public int getValue(){
return _area.getValue();
}
protected void hideNotify(){
_area.stop();
}
public void invalidateCounter( CounterArea counter )
if( counter == _area ){
invalidate();
}
}
protected void paint( Graphics g, int width, int height ){
_area.paint( g );
}
public void repaintCounter( CounterArea counter ){
if( counter == _area ){
repaint();
}
}
public void resizeCounter( CounterArea counter ){
if( counter == _area ){
invalidate();
}
}
protected void sizeChanged( int w, int h ){
_area.setWidth( w );
_area.setHeight( h );
}
public void setFont( Font f ){
_area.setFont( f );
}
public void setValue( int value ){
_area.setValue( value );
}
protected void showNotify(){
_area.start();
}
public boolean traverse( int dir, int vw, int vh,
int[] vrect ){
return false;
}
private CounterArea _area;
}
外覆类只是起到接收回递函数给控制段,自动开始和关闭计数器的作用,当然,任何时候如果这个Item显示或者隐藏,你都希望能够改变它的行为。
最后, 是一个简单的测试 MIDlet去验证这个计数器的作用:
import java.io.*;
import java.util.*;
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
// A simple MIDlet to test the custom counter
// component.
public class CounterItemTest extends MIDlet
implements CommandListener {
private Display display;
public static final Command exitCommand =
new Command( "Exit",
Command.EXIT, 1 );
public CounterItemTest(){
}
public void commandAction( Command c,
Displayable d ){
if( c == exitCommand ){
exitMIDlet();
}
}
protected void destroyApp( boolean unconditional )
throws MIDletStateChangeException {
exitMIDlet();
}
public void exitMIDlet(){
notifyDestroyed();
}
public Display getDisplay(){ return display; }
protected void initMIDlet(){
Form f = new Form( "CounterItem Test" );
f.addCommand( exitCommand );
f.setCommandListener( this );
CounterItem counter = new CounterItem();
counter.setLayout( Item.LAYOUT_CENTER
Item.LAYOUT_NEWLINE_BEFORE
Item.LAYOUT_NEWLINE_AFTER );
f.append( counter );
getDisplay().setCurrent( f );
}
protected void pauseApp(){
}
protected void startApp()
throws MIDletStateChangeException {
if( display == null ){
display = Display.getDisplay( this );
initMIDlet();
}
}
}
原文作者: Eric Giguere
原文地址:http://developers.sun.com/techtopics/mobility/midp/ttips/counteritem/
(出处:http://www.knowsky.com)