Java ME: Midlet Structure

In this article we will analyze in detail one of the options for constructing the structure of the midlet. This material will be useful for beginners.

Imagine the following situation


We have 3 screens:

  1. Splash screen (which will be displayed first);
  2. Menu screen;
  3. Game screen.

For example, I will not write 3D games and menus with animation of space battles, because it will only be distracting. Each of the screens will perform the following actions:

  • “Splash” screen - displays the text “SPLASH” for 10 seconds;
  • Menu screen - displays the text “MENU” for 10 seconds;
  • The "Game" screen - displays the text "GAME".

So, already having this information, for our screens we can choose a common abstract class, and its name is “Screen”.

What do all screens have in common?

  • Each screen has its own maximum sizes;
  • Each screen has its own object for drawing;
  • Since we draw on the screen only after it is installed on the display, we can determine the general event:
  • start - called after setting the screen to Display.


Abstract class for all screens.


public abstract class Screen extends GameCanvas {
    protected final int screenWidth; //ширина экрана
    protected final int screenHeight; //высота экрана
    protected final Graphics graphics; //объект для рисования
    public Screen() {
        super(false);
        setFullScreenMode(true);
        screenWidth = getWidth();
        screenHeight = getHeight();
        graphics = getGraphics();
    }
    //метод, вызывающийся первым, после установки экрана на дисплей
    public abstract void start();
}

We inherited our common interface from GameCanvas and made some changes to it: We
added our own fields with the protected modifier and defined a common event.
protected fields can only see child classes.
abstract in a method definition means that the method is abstract. That is, we do not write the body of the method, but we know for sure that the descendants implement it (unless they are abstract). If the method contains at least one abstract method, the class must be marked abstract, otherwise the compiler will throw an error.

Further, creating our screens, we will inherit them from Screen, and not from GameCanvas. We get the following hierarchy:

Object -> GameCanvas -> Screen -> MyScreen

In Java, all classes are implicitly inherited from Object. Screen is inherited from GameCanvas in order to remain a “canvas” for drawing and handling clicks (although this is far from all). All of our screens will inherit from Screen in order to define a common interface. It would not be entirely correct to write the same thing in each class, especially since we will need a common interface for another construction, which you will learn about later.

Main class


public class MIDletStructure extends MIDlet {
    public static MIDletStructure midlet; //указывает на себя
    private final Display display;
    public MIDletStructure() {
        midlet = this;
        display = Display.getDisplay(this);
    }
    //установить новый экран
    public void setCanvas(Screen canvas) {
        display.setCurrent(canvas);
        canvas.start();
    }
    public void startApp() {}
    public void pauseApp() {}
    public void destroyApp(boolean unconditional) {}
}

In the main class, we define the fields midlet and display. The first refers to itself, and the second is a link to Display. The midlet link is needed in order to communicate with the main class. For example, to turn off the application or set a different screen, which is what we will do later.

Let us consider the setCanvas method in more detail: the setCanvas method accepts in the parameter a link to any class inherited from Screen, then, since Screen is inherited from GameCanvas (which in turn is inherited from Displayable), it sets it to the display and starts it using the start method. The whole hitch in Screen - we can pass an instance of any class inherited from Screen to the method, whether it is a menu screen or a game, and thanks to the common interface we know for sure that any instance has an implemented start method. All this is done thanks to late binding in Java, if not for it, then for each descendant class of Screen it would be necessary to write a separate method.

First screen


public class Splash extends Screen {
    //метод, вызывающийся первым, после установки экрана на дисплей
    public void start() {
        graphics.setColor(0xFFFFFF);
        graphics.fillRect(0, 0, screenWidth, screenHeight);
        graphics.setColor(0x000000);
        graphics.drawString("SPLASH", 0, 0, 0);
        flushGraphics();
        try {
            Thread.sleep(10000);
        } catch (InterruptedException interruptedException) {
            System.out.println(interruptedException.getMessage());
            MIDletStructure.midlet.notifyDestroyed();
        }
    }
}

It is easy to see that in the start method we already use the classes that were initialized in our ancestor class.

Now we write a similar class for the game screen:

public class Game extends Screen {
    //метод, вызывающийся первым, после установки экрана на дисплей
    public void start() {
        graphics.setColor(0xFFFFFF);
        graphics.fillRect(0, 0, screenWidth, screenHeight);
        graphics.setColor(0x000000);
        graphics.drawString("GAME", 0, 0, 0);
        flushGraphics();
        while (true) {
            try {
                Thread.sleep(20);
            } catch (InterruptedException interruptedException) {
                System.out.println(interruptedException.getMessage());
                MIDletStructure.midlet.notifyDestroyed();
            }
        }
    }
}

And add links to our screens in the main class:

public class MIDletStructure extends MIDlet {
    public static MIDletStructure midlet; //указывает на себя
    private final Display display;
    public final Screen splash; //экран “сплэш”
    public final Screen menu; //экран меню
    public final Screen game; //экран игры
    public MIDletStructure() {
        midlet = this;
        display = Display.getDisplay(this);
        splash = new Splash();
        menu = new Menu();
        game = new Game();
    }
    //установить новый экран
    public void setCanvas(Screen canvas) {
        display.setCurrent(canvas);
        canvas.start();
    }
    public void startApp() {
        //устанавливаем первый экран
        setCanvas(splash);
    }
    public void pauseApp() {}
    public void destroyApp(boolean unconditional) {}
}

Let's pay attention to the midlet link: it is statistical, public and points to an instance of the class in which it is located. Why is this needed? public means that the link can be accessed anywhere in the program, static means that the link can be accessed through the class (Main.midlet), and since all screen fields and the setCanvas method are also public, therefore, the screen can be replaced anywhere programs:

Main.midlet.setCanvas(Main.midlet.game);

Conclusion


After the first reading, the beginner probably will not understand a single word, however, with a more thoughtful reading, you can notice all the simplicity of the algorithm and its logical solution using OOP technology. In the article I tried to describe in detail everything that is necessary, but if you still have questions or inaccuracies, you can see the full sources below:

Full sources

Also popular now: