使用MIDP(Mobile Information Device Profile)的开发人员经常会抱怨用些什么办法才可以在一个MIDlet上显示动画。 MIDP 1.0 没有直接提供对动画的支持(正在开发中的MIDP 2.0支持),但真要是自己去实现,其实也并非是一件很难的事。
{QD/X+55. 任何动画的最基本的前提,是要在足够快的时间内显示和更换一张张的图片,让人的眼睛看到动的画面效果。图片必须按照顺序画出来。从一张图片到下一张图片之间的变化越小,效果会越好。
9hn8TN`/ 首先要做的,是使用你的图片处理软件(比如ps或者firework)创建一系列相同大小的图片来组成动画。每张图片代表动画一帧。需要制作一定数量的祯--越多的帧会让你的动画看上去越平滑。制作好的图片一定要保存成PNG(Portable Network Graphics)格式,MIDP唯一支持的图片格式;有两个办法让你刚做好的图片在MIDlet上变成动画。第一,把图片都放到一个web服务器上,让MIDlet下载他们,MIDP内置的HTTP支持。第二个办法更简单,把图片用MIDlet打包成jar文件。如果你使用的是J2ME开发工具,把PNG文件放入你的项目文件里面就可以了。
*_"9.@ 动画的过程其实更像帐本记录:显示当前帧,然后适当地更换到下一帧。那么使用一个类来完成这个工作应该是很恰当的,那好,我们就先定义一个AnimatedImage类:
j9*ok7A(^
.0mo}
import java.util.*; -+^O#`$Hm
import javax.microedition.lcdui.*; M-6dF>f/`C
// 定义了一个动画,该动画其实只是一系列相同大小的图片 Bg=i.;.
// 轮流显示,然后模拟出的动画 p:gTMJ:uB
public class AnimatedImage extends TimerTask {; 'VkM`l(lj
private Canvas canvas; $pm}z:cwE
private Image[] images; 3Ct@p=Dc
private int[][] clipList; V;NW8yA
private int current; "0yK/
private int x; ,snm4
private int y; d`*P7s
private int w; AJ jJ#2ZB
private int h; jSX-z@kso
// Construct an animation with no canvas. .'w"N1_.
public AnimatedImage( Image[] images ){; 14Y`{8
this( null, images, null ); pUgo2{&
}; ||q/9_oSF
// Construct an animation with a null clip list. {zJ#uh
public AnimatedImage( Canvas canvas, Image[] D{a8?aV
images ){; this( canvas, images, null ); z* u`.
}; 97M0MN8
// Construct an animation. The canvas can be null, 6XwhaA.
// but if not null then a repaint will be triggered ?1Z@nw-X9
// on it each time the image changes due to a timer 1{y)iiaO>
// event. If a clip list is specified, the image is fDI* 'k>D
// drawn multiple times, each time with a different Q`6o`vT3
// clip rectangle, to simulate transparent parts. DVQi5kL
public AnimatedImage( Canvas canvas, Image[] images, MqIAll;
int[][] clipList ){; Y+^ Boko=N
this.canvas = canvas; v*%)GM
this.images = images; F/%3(z41
this.clipList = clipList; jm;heW1<
if( images != null && clipList != null ){; s++b;K
if( clipList.length < images.length ){; Q7peyuG
throw new IllegalArgumentException(); iDv>;R^]Z=
}; SsRfo"
}; VVq/LL90
if( images != null && images.length > 0 ){; y Z*kgE
w = images[0].getWidth(); bIz7A4/
h = images[0].getHeight(); KHUip1/_
}; v@v YQG/
}; Q- xRiKVy
// Move to the next frame, wrapping if necessary. m?jw+Ul'
public void advance( boolean repaint ){; M8qz_1*9u
if( ++current >= images.length ){; kzCf%a=J
current = 0; L]]^=wf
}; dmwrWmQ
if( repaint && canvas != null && canvas.isShown() CU:)D2N
){; +<_?ZY+&N
canvas.repaint( x, y, w, h ); p4ND: g`
canvas.serviceRepaints(); 0u[9zz
}; 44AhT[x
}; S^]i}HeZ&
// Draw the current image in the animation. If m>XUnv<fy
// no clip list, just a simple copy, otherwise s_ZQ
// set the clipping rectangle accordingly and cFF?+wqR
// draw the image multiple times. ]bQ=_W X
public void draw( Graphics g ){; ;w&WCd?;
if( w == 0 || h == 0 ) return; Y{ r^L
int which = current; kW:nyvX
if( clipList == null || clipList[which] == null DfoJm+oy
){; sZ7rA_z n
g.drawImage( images[which], x, y, Ogi-@=
g.TOP | g.LEFT ); ,7;&O*vF ,
}; else {; mVfae}3R>{
int cx = g.getClipX(); c%^t6 pD
int cy = g.getClipY(); >=F nv=
int cw = g.getClipWidth(); c(m J0
int ch = g.getClipHeight(); uXW!p--
int[] list = clipList[which]; #@5~b p
for( int i = 0; i + 3 <= list.length; i += IiM pP
4 ){; fZ)h ]!
g.setClip( x + list[0], y + list[1], tj6h
list[2], list[3] ); |PE]3I
g.drawImage( images[which], x, y, 'd$}+u[<
g.TOP | g.LEFT ); hLp+zcsN
}; j> gn4xA
g.setClip( cx, cy, cw, ch ); u$t(c@
}; _/i#-IK
}; ftw3G#
// Moves the animation´s top left corner. _zyk@79(
public void move( int x, int y ){; E="gG.Al
this.x = x; /62)JE *
this.y = y; 5jd!WAX
}; mQe$h61%M
// Invoked by the timer. Advances to the next frame #tM.PBe~L
// and causes a repaint if a canvas is specified. Exd Vv
public void run(){; l>EV~mDy
if( w == 0 || h == 0 ) return; ~ -q)s
advance( true ); aN['8ql,
}; v;3crJ(>e
}; ~*$pWQzw0
SQ31z>r.o ]1axC5 ~ 你实例化一个AnimatedImage对象的时候你必须给AnimatedImage类的构造方法传一个Image对象数组,该数组代表动画的每一帧。
5vDG#s^F 使用的所有图片必须具有相同的高度和宽度。用Image.createImage()方法从jar文件里面加载图片:
? b!K /$
*,0Z)JVM
private Image[] loadFrames( String name, int frames ) G=O&{r
throws IOException {; $kYztY0e
Image[] images = new Image[frames]; {hJ. -pJ
for( int i = 0; i < frames; ++i ){; ]jc j$
images = Image.createImage( name + i + ^N&sPh~
".png" ); |H#6[/o`
}; "I`?e|r
return images; BW g_G]a|
}; yy}s5W1He
L<'N^Ome 你也可以传递一个Canvas对象(可选),和一个剪辑列表(clip list)。如果你指定了一个canvas和使用一个timer来自动更换到动画的下一帧,就如下面的例子代码中一样,canvas在动画向前滚动以后自动被重画(repaint)。不过这样的实现办法是可选的,你可以这样做,也可以让程序选择合适的时候重画canvas。
t9/XOSo@o 因为MIDP 1.0不支持透明的图片,AnimatedImage 类使用一个剪辑列表来模拟透明的效果,剪辑列表是图片被剪成的方块区域的系列。图片被画出来的时候分开几次,每次画一个剪辑列表里面的剪辑区域。剪辑列表在帧的基础上被定义好,所以你需要为图片的每一帧创建一个数组。数组的大小应该是4的倍数,因为每一个剪辑面积保持了四个数值:左坐标,顶坐标,宽度以及高度。坐标的原点是整个图片的左上角。需要注意的是使用了剪辑列表会使动画慢下来。如果图片更加复杂的话,你应该使用矢量图片。
f4fY91!V AnimatedImage类扩展了java.util.TimerTask,允许你设定一个timer。这里有个例子说明如何使用timer做动画:
`a. [h Timer timer = new Timer();
B0r`9BI3 AnimatedImage ai = ..... // get the image
'Vd^UN#zg timer.schedule( ai, 200, 200 );
Lu~ Ui[ 2 每隔大约200毫秒,timer调用AnimatedImage.run()方法一次,这个方法使得动画翻滚到下一个帧。
C5x4pB9 现在我们需要的是让MIDlet来试试显示动画!我们定义一个简单的Canvas类的子类,好让我们把动画“粘贴上去”。
gTJhZ,dH
RB&Q)YK6
import java.util.*; 36]pC7sI
import javax.microedition.lcdui.*; lGk"V>GF
// A canvas to which you can attach one or more <1|{i+c
// animated images. When the canvas is painted, m_),}n#P
// it cycles through the animated images and asks &~w=n
// them to paint their current image. GU[ 3.q<py
public class AnimatedCanvas extends Canvas {; Ux&b $EGe
private Display display; k'dy";
private Image offscreen; WoR$? %
private Vector images = new Vector(); 7l=/"d0BL
public AnimatedCanvas( Display display ){; /u o% 3
this.display = display; ijQ#(YI
// If the canvas is not double buffered by the ,qQy<)~,!
// system, do it ourselves... = z2Bfea
if( !isDoubleBuffered() ){; HAQ8 +
offscreen = Image.createImage( getWidth(), yhb@"Kr
getHeight() ); 67/#~j!g
}; <lXitv^C
}; 0?%u#$;N9S
// Add an animated image to the list. i?:7[h-J
public void add( AnimatedImage image ){; RnX PM0Y
images.addElement( image ); KM<EjQ
}; HW $X`
// Paint the canvas by erasing the screen and then K^D9oos
// painting each animated image in turn. Double mF-&q&K
// buffering is used to reduce flicker. y]x{x)M
protected void paint( Graphics g ){; 6o|3nw3B
Graphics saved = g; ;gv0i #
if( offscreen != null ){; '>2~Q37#l
g = offscreen.getGraphics(); = A7{<N
}; d^u{3?`tj!
g.setColor( 255, 255, 255 ); EXMzQ>EnZ
g.fillRect( 0, 0, getWidth(), getHeight() ); (`ZKTQh
int n = images.size(); $e4hQ!!
for( int i = 0; i < n; ++i ){; UtU,g@cT4
AnimatedImage img = (AnimatedImage) P3g +Q
images.elementAt( i ); USb2zUP_
img.draw( g ); N)[Ti=
}; "jD'7BM2M
if( g != saved ){; ^p_<ED~DU
saved.drawImage( offscreen, 0, 0, KM;]JE"
Graphics.LEFT | Graphics.TOP ); ^;%any#ME
}; %~zcxz]
}; *R=}zguR)I
}; cW 6|pK]
`)R-iBQ= AnimatedCanvas 类的代码相当简单,由一个动画导入方法和一个paint方法。canvas画布每次被画,背景都会被擦除,然后循环每个导入的AnimatedImage对象,直接画到自己身上来(自己扩展了canvas类)。
n]&@;kcT#O
Yei*2EP
import java.io.*; `KK0KD
import java.util.*; -+LY)L>
import javax.microedition.lcdui.*; e,1 > x
import javax.microedition.midlet.*; k fS=x$&
// MIDlet that displays some simple animations. AW=<E)
// Displays a series of birds on the screen and mS>0cM"/
// animates them at different (random) rates. GQNp5M3
public class AnimationTest extends MIDlet G! 225h
implements CommandListener {; o_>Z
private static final int BIRD_FRAMES = 7; tpG=,3
private static final int NUM_BIRDS = 5; ^}Fg8fd
private Display display; -eYc~Y5
private Timer timer = new Timer(); M2Qd Shz%
private AnimatedImage[] birds; O894v}e
private Random random = new Random(); @ .)+Hn
public static final Command exitCommand = dMyatcc
new Command( "Exit", &pz9"[ E
Command.EXIT, 1 ); q22K}W?
public AnimationTest(){; H`o.c^c]
}; >$x|A;~An
public void commandAction( Command c, y%[p4LG-*/
Displayable d ){; 5^r*y
if( c == exitCommand ){; [>4.i^w
exitMIDlet(); e!)FpT
}; 7LxUME)}p
}; L xzF8o7
protected void destroyApp( boolean unconditional ) 1[r" 0
throws MIDletStateChangeException {; Mmd}8'e,O+
exitMIDlet(); VjL{;G37t
}; LUoXhE%H
public void exitMIDlet(){; zlwvp:b
timer.cancel(); // turn it off... u{(K_+y
notifyDestroyed(); [z4f^^
}; =THY/IP5U
// Generate a non-negative random number... dZkEh`6Vf
private int genRandom( int upper ){; Ky6ET%Y9
return( Math.abs( random.nextInt() ) % upper ); ctu%O;=j
}; ~Ib#`8Uy
public Display getDisplay(){; return display; }; 2+[]ON$lS
// Initialize things by creating the canvas and then Y Cx]d*kN
// creating a series of birds that are moved to 1.kP[J
// random locations on the canvas and attached to F9?:(hma'
// a timer for scheduling. cqX[nZ
protected void initMIDlet(){; ANc( X U
try {; bv|`/|
AnimatedCanvas c = new p[66+$rU
AnimatedCanvas( getDisplay() ); OJE<9K_L
Image[] images = v+wiB`x
loadFrames( "/images/bird", Wh+pk
BIRD_FRAMES ); OEV0C
int w = c.getWidth(); P}'cr
int h = c.getHeight(); xm?`8{rcF)
birds = new AnimatedImage[ NUM_BIRDS ]; {26703_fy
for( int i = 0; i < NUM_BIRDS; ++i ){; `i'@Eg*
AnimatedImage b = new }3b Wf
AnimatedImage( c, images ); 'QyOQ7;
birds = b; vCvlBy~
b.move( genRandom( w ), genRandom( h ) ); V7g-dho
c.add( b ); UG#&1i}Z
timer.schedule( b, genRandom( 1000 ), mU_H0
genRandom( 400 ) ); h;_B^'p#
}; 8 lo9HyYc.
c.addCommand( exitCommand ); c %f:.=
c.setCommandListener( this ); -Y",f$
getDisplay().setCurrent( c ); Ya.' Xx| ,
}; <4J2Ly/4V
catch( IOException e ){; ?YGr,RB
System.out.println( "Could not Z5umZ#ilH
load images" ); D5jP=a#
exitMIDlet(); Z>|PH=JT!
}; K'B kPT
}; _":j-tl[L
// Load the bird animation, which is stored as a 5<x5[z=
// series of PNG files in the MIDlet suite. &kS10 6b<
private Image[] loadFrames( String name, int frames ) < R`)+If
throws IOException {; Ll+"^kI ^Q
Image[] images = new Image[frames]; Z hMb-yf
for( int i = 0; i < frames; ++i ){; .IC*5bzF+
images = Image.createImage( name + :@@Ao~ej
i + ".png" ); z|NJ^a"y
}; W:ftr2piW
return images; 1Pi KIp
}; }fNu"2Z
protected void pauseApp(){; T^B$9
}; =*9Tat81
protected void startApp() H^]yR1_**
throws MIDletStateChangeException {; SOW7G T1[
if( display == null ){; G2yvRm
display = Display.getDisplay( this ); *&BT35Jlzp
initMIDlet(); 1m:bwj"
}; eTP! $D
}; @gB~V.CO+Q
}; ;X!gq:c
lT?.l: DUx 七帧图片的动画,你可以看到一个拍着翅膀的小鸟。MIDlet显示了5只小鸟,小鸟的位置和刷新速度是随机的。你可以用一些其他的办法来改进这个程序,但这个程序也应该足够能让你上手了。
用户评论