在我前面的LWUIT的文章LWUIT显示gif动画中,介绍了如何在LWUIT中使用动画的方法,不过在那种方法中必须使用gif格式的图片,而且必须要在resource editor中进行设置,虽然我们不需要些多少代码就能够现实动画,但是在LWUIT中是有办法通过代码做出动画的,我们仅仅需要的是一张png图片。
在这个例子中,我使用的是如下的一张png图片。
如何让这张图片能像gif一样能动呢?我们知道gif其实是由几张不同的图片组合而来的,每张图片是gif的一帧。很明显这张图片要做出的是旋转效果,我们的目的就是对这张图片进行不同角度的旋转。如何旋转呢?通过LWUIT的doc中我找到一个Image的方法rotate(int degrees),这个方法通过传入一个角度,对返回图片旋转后的实例。
既然知道方法了,但是在LWUIT中图片不能单独显示的,必须有一个组件最为媒介才能让图片显示,再看看Component类的文档,发现它继承了Animation接口,继续看这个接口中的方法,竟然发现了paint(Graphics g)方法,说明在页面中显示的动画是通过画笔来实现的,同样所有的组件(都继承自Component)都有paint方法。
通过上述解释,我们找到制作动画的方法和媒介了,那我们就动手来实现它。
/** * A "washing machine" progress animation that rotates the given image as an * animation. The image for rotation must be square. * * @author Sunny */ public class InfiniteProgressIndicator extends Component { private Image[] angles; private int angle; public InfiniteProgressIndicator(Image image) { Image fourtyFiveDeg = image.rotate(45); angles = new Image[] {image, fourtyFiveDeg, image.rotate(90), fourtyFiveDeg.rotate(90), image.rotate(180), fourtyFiveDeg.rotate(180), image.rotate(270), fourtyFiveDeg.rotate(270)}; getStyle().setBgTransparency(0); } protected Dimension calcPreferredSize() { Style s = getStyle(); return new Dimension(angles[0].getWidth() + s.getPadding(LEFT) + s.getPadding(RIGHT), angles[0].getHeight() + s.getPadding(TOP) + s.getPadding(BOTTOM)); } public void initComponent() { getComponentForm().registerAnimated(this); } public void paint(Graphics g) { Style s = getStyle(); g.drawImage(angles[Math.abs(angle % angles.length)], getX() + s.getPadding(LEFT), getY() + s.getPadding(TOP)); } public boolean animate() { angle++; return true; } }
对上面的方法我做下简要的说明:
InfiniteProgressIndicator(Image image)构造方法,传入一张图片,首先得到这张图片的45°旋转角图片,然后通过这两张图片得到另外6个角度的图片,这8张图片的角度分别为:北,东北,东,东南,南,西南,西,西北,每个角度间隔45°。可能各位有个问题,为什么不一次性对传进来的图片进行45°,90°,135°,180°,225°,270°,325°的旋转呢?答案就在文档中,文档中有一段解释:rotating an image to 45, 90 and 135 degrees is inefficient. Use rotatate to 45, 90 and then rotate the 45 to another 90 degrees to achieve the same effect with less memory.这样做是为了节省内存。
calcPreferredSize()方法,计算Component的适应宽度,主要是因为原来的Component类有Padding。
initComponent() 方法,在该Component所属的Form中注册动画效果。
paint()方法和animate()方法结合起来使用,不断的重绘图片,达到动画效果。
怎么样,代码还是很简单的。
然后我们看看使用方法:
/** * @author Sunny */ public class AnimateMidlet extends MIDlet { public void startApp() { Display.init(this); Form f = new Form("Animate test"); f.setLayout(new BorderLayout()); try{ Image icon = Image.createImage("/wait-circle.png"); InfiniteProgressIndicator indicator = new InfiniteProgressIndicator(icon); f.addComponent(BorderLayout.CENTER,indicator); }catch(IOException ex){ ex.printStackTrace(); } f.show(); } public void pauseApp() { } public void destroyApp(boolean unconditional) { }
}