Nokia UI学习范例FishTank研究

作者:mingjava  文章来源:http://www.j2medev.com/Article/ShowArticle.asp?ArticleID=153

由于MIDP1.0中提供的API比较有限,因此Nokia实现了自己的类库来提供补充。其中包括声音的支持、屏幕的背景光和机器震动、图像的翻转、Alpha通道以及对像素的操作支持。学习Nokia UI的使用的最好例子莫过于Nokia官方提供的FishTank了。本文讲主要介绍一下这个范例,其中的一些思路和技巧是值得我们学习的。

FishTank的界面比较简单,只有一个屏幕。池塘里的鱼儿不停的游动,水底的水草也不停的漂浮。这个界面是分层设计的,因此引入了一个变量z,这样就和水平参数x和垂直参数y构成了三维的空间。其中水草处于中间层。鱼的游动是无规律的,其中的算法我们可以参考,当鱼触及到边缘的时候,它会反向游动。通过Nokia UI中提供的水平翻转可以实现。 dg.drawImage(img, x, y, (Graphics.LEFT | Graphics.TOP),DirectGraphics.FLIP_HORIZONTAL);水草的游动也是随机的

 

    下面我们看看整个应用的结构

 

 

 

 

 

 

 

程序总共有四个类组成,其中我们主要关注的是FishTankCanvas、Fish和Weeds。其中Fish和Weeds比较相似,他们代表了两个对象鱼儿和水草。而FishTankCanvas是FullCanvas的子类并且实现了Runnable接口,它是一个独立的线程。每隔一段时间他会更新一下Fish和Weeds的状态,然后重新绘制屏幕。run()方法中的代码如下
    public void run()
    {
        Thread currentThread = Thread.currentThread();

        try
        {
            // This ends when animationThread is set to null, or when
            // it is subsequently set to a new thread; either way, the
            // current thread should terminate
            while (currentThread == animationThread)
            {
                long startTime = System.currentTimeMillis();

                // Only animate when the canvas is visible.
                if (isShown())
                {
                    tick();

                    // Repaint everything above the sand, the fish
                    // never swim at h > waterHeight.
                    repaint(0, 0, waterWidth, waterHeight);
                    serviceRepaints();
                }

                long timeTaken = System.currentTimeMillis() - startTime;
                if (timeTaken < MILLIS_PER_TICK)
                {
                    synchronized (this)
                    {
                        wait(MILLIS_PER_TICK - timeTaken);
                    }
                } else
                {
                    currentThread.yield();
                }
            }
        } catch (InterruptedException e)
        {
        }
    }
由于在屏幕的下方有一部分是泥土,因此在这里调用了repaint(0,0,waterWidth,waterLength)。其实FishTankCanvas同时也是一个容器类,它是Fish和Weeds的容器。他去更新Fish和Weeds的状态然后调用各自的draw()方法来重新绘制。下面我们看看FishTankCanvas是如何绘制的
    private synchronized void drawFishTank(Graphics g)
    {
        // Draw the water
        g.setColor(0, 255, 255);
        g.fillRect(0, 0, waterWidth, waterHeight);

        // Draw the sand
        g.setColor(255, 128, 64);
        g.fillRect(0, waterHeight, waterWidth, getHeight());

        // Draw the weeds and fishes
        for (int plane = 0; plane < NUM_PLANES; plane++)
        {
            if (plane == WEEDS_PLANE)
            {
                weeds.draw(g);
            }

            for (int i = 0; i < fishes.size(); i++)
            {
                Fish fish = (Fish) (fishes.elementAt(i));
                if (fish.getZ() == plane)
                {
                    fish.draw(g);
                }
            }
        }
    }

    接下来我们研究一下Fish和Weeds的代码,它们就是有很多图片构成的。实现的结果和MIDP2.0中的Sprite差不多。Fish和Weeds的图片都是有类型之分的,每个类型又有几个桢。这样才可以构造出不同的对象,并且相同的对象在不同的时刻也有不同的状态。
    private static final int TYPE_NUM = 3;

    private static final int FRAME_NUM = 2;

    private static final Image[][] fishImage;
Fish和Weeds都是采用了静态初始化块的方式来初始化相应的二维数组。例如
    static
    {
        fishImage = new Image[TYPE_NUM][FRAME_NUM];

        int i = 0;
        int k = 0;
        try
        {
            for (i = 0; i < TYPE_NUM; i++)
            {
                for (k = 0; k < FRAME_NUM; k++)
                {
                    fishImage[i][k] = Image.createImage("/fish" + i + k
                            + ".png");
                }
            }
        } catch (java.io.IOException e)
        {
            fishImage[i][k] = null;
        }

        // This MIDlet implicitly assumes that all the Images
        // are the same width and height.

        fishWidth = fishImage[0][0].getWidth();
        fishHeight = fishImage[0][0].getHeight();
    }

为了让鱼儿或者水草具有更好的随机性,代码是通过如下的算法来控制他们的方向的,下面以方向x为例说明
        // mostly continue as we are, but sometimes randomly change
        if ((ticksSinceLastChangeX > 20) && (rand(20) == 0))
        {
            vx = rand(2) * 2 - 1; // -1 or 1
            ticksSinceLastChangeX = 0;
        }

        // if moving would take us off the left or right of the water,
        // reverse
        if (((vx < 0) && (x + vx < 0))
                || ((vx > 0) && (x + fishWidth + vx > FishTankCanvas.waterWidth)))
        {
            vx = -vx;
            ticksSinceLastChangeX = 0;
        }
当鱼接触到水的边缘的时候,它的速度方向会改变。在此前的代码是为了增加他的随机性。Fish的重新绘制也比较简单,这里用到了Nokia UI中的水平翻转的扩展功能
    public void draw(Graphics g)
    {
        DirectGraphics dg = DirectUtils.getDirectGraphics(g);

        Image img = fishImage[type][frame];
        if (img != null)
        {
            if (vx < 0)
            {
                dg.drawImage(img, x, y, (Graphics.LEFT | Graphics.TOP),
                        DirectGraphics.FLIP_HORIZONTAL);
            } else
            {
                dg.drawImage(img, x, y, (Graphics.LEFT | Graphics.TOP), 0);
            }
        }

        frame = (frame + rand(2)) % FRAME_NUM;
    }

我们可以看到每次绘画结束后都会把frame的编号随机设置一下,这样可以得到鱼儿本身运动的效果。

    MIDlet的代码比较简单,直接给出
package example.fishtank;

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public class FishTankMIDlet extends MIDlet
{
    private final FishTankCanvas canvas;

    public FishTankMIDlet()
    {
        canvas = new FishTankCanvas(this);
    }

    public void startApp()
    {
        Display.getDisplay(this).setCurrent(canvas);
        canvas.start();
    }

    public void pauseApp()
    {
        canvas.stop();
    }

    public void destroyApp(boolean unconditional)
    {
        canvas.stop();
    }

    /*
     * On the exit command, cleanup and notify that the MIDlet has been
     * destroyed.
     */
    void exitRequested()
    {
        destroyApp(false);
        notifyDestroyed();
    }
}

    总结:我们在本文中可以学到如何使用Nokia UI中提供给我们的扩展功能,比如水平翻转、全屏显示等。更重要的是,FishTank的代码设计比较合理,代码思路清晰。比较容易维护和扩展。另外程序中随机性的算法也可以用于实际工作中。您可以从这里下载全部代码点击浏览该文件

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值