Структура мидлета [Java 2 ME]
от Kalter
В статье мы подробно разберём один из вариантов построения структуры мидлета.
Для удобства я не стану приводить полный код (полные исходники вы можете скачать по ссылке ниже).
Представим себе следующую ситуацию
У нас есть 3 экрана:
1. Экран "Сплэш" (который будет отображаться первым)
2. Экран "Меню"
3. Экран "Игра"
Для примера я не буду писать игры и меню с анимацией, так что каждый из экранов будет выполнять следующие действия:
Экран "Сплэш" - выводит на 10 секунд текст "SPLASH"
Экран "Меню" - выводит на 10 секунд текст "MENU"
Экран "Игра" - выводит текст "GAME"
Итак, имея уже эти сведения, для наших экранов можно подобрать общий абстрактный класс, и имя ему - "Screen"
Что общего между всеми экранами?
У каждого экрана есть свои максимальные размеры
У каждого экрана есть свой объект для рисования
Так как мы рисуем на экране только после его установки на дисплей, то можно определить общее событие:
start - вызывается после установки экрана на Дисплей
Абстрактный класс для всех экранов
Мы унаследовали наш общий интерфейс от GameCanvas и внесли в него некоторые изменения:
Добавили собственные поля с модификатором protected и определили общее событие.
protected поля могут видеть только классы-потомки.
abstract в определении метода означает, что метод является абстрактным. То есть мы не пишем тело метода, но точно знаем, что потомки его реализуют (если только они не будут абстрактными). Если метод содержит как минимум один абстрактный метод, то класс должен быть помечен абстрактным, иначе компилятор выдаст ошибку.
Далее, создавая наши экраны, мы будет наследовать их уже от Screen, а не от GameCanvas. У нас получается следующая иерархия:
Object -> GameCanvas -> Screen -> НашЭкран
В Java все классы (даже неявно) наследуются от Object. Screen унаследован от GameCanvas, чтобы оставаться "холстом" для рисования и обработки нажатий (хотя это далеко не всё). Все наши экраны му будем наследовать от Screen для того, чтобы определить общий интерфейс. Было бы не совсем правильно писать одно и то же в каждом классе, тем более что общий интерфейс нам понадобится ещё для одной конструкции, о которой вы узнаете позже
Главный класс
В главном классе определяем поля midlet и display. Первый ссылается на самого себя, а второй представляет собой ссылку на Display. На более подробном описании ссылки midlet мы остановимся чуть позже
Рассмотрим метод setCanvas подробнее: метод setCanvas принимает в параметре ссылку на любой класс, унаследованный от Screen, затем, так как Screen унаследован от GameCanvas, то устанавливает его на дисплей и с помощью метода start запускает его. Вся загвоздка в Screen - мы можем передать в метод экземпляр любого класса, унаследованного от Screen, будь то экран меню, или игры, а благодаря общему интерфейсу мы точно знаем, что у любого экземпляра есть реализованный метод start. Всё это осуществляется благодаря позднему связыванию в Java, если бы не оно, то для каждого класса-потомка Screen пришлось бы писать отдельный метод.
Первый экран
Легко заметить, что в методе start мы уже пользуемся классами, которые инициализировали в нашем классе-предке.
Теперь пишем подобный класс для экрана игры:
И добавляем ссылки на наши экраны в главный класс:
Обратим внимание на ссылку midlet: она является статистической, публичной и указывает на экземпляр класса, в котором находится. Зачем это нужно? public обозначает, что к ссылке можно обратится в любом месте программы, static обозначает, что к ссылке можно обратится через класс (Main.midlet), а так как все поля экранов и метод setCanvas являются тоже публичными, следовательно, экран можно заменить в любом месте программы:
После первого прочтения новичок, наверное, не поймёт ни единого слова, однако при более вдумчивом чтении можно заметить всю простоту алгоритма и его логичное решение, используя технологию ООП. В статье я постарался описать детально всё, что необходимо, однако если у вас остались вопросы или неточности, то задавайте их в комментариях. Так же вы можете протестировать данный код, скачав полные исходники.
Полные исходники: MIDletStructure.zip
Для удобства я не стану приводить полный код (полные исходники вы можете скачать по ссылке ниже).
Представим себе следующую ситуацию
У нас есть 3 экрана:
1. Экран "Сплэш" (который будет отображаться первым)
2. Экран "Меню"
3. Экран "Игра"
Для примера я не буду писать игры и меню с анимацией, так что каждый из экранов будет выполнять следующие действия:
Экран "Сплэш" - выводит на 10 секунд текст "SPLASH"
Экран "Меню" - выводит на 10 секунд текст "MENU"
Экран "Игра" - выводит текст "GAME"
Итак, имея уже эти сведения, для наших экранов можно подобрать общий абстрактный класс, и имя ему - "Screen"
Что общего между всеми экранами?
У каждого экрана есть свои максимальные размеры
У каждого экрана есть свой объект для рисования
Так как мы рисуем на экране только после его установки на дисплей, то можно определить общее событие:
start - вызывается после установки экрана на Дисплей
Абстрактный класс для всех экранов
- public abstract class Screen extends GameCanvas{
- protected final int screenWidth; //ширина экрана
- protected final int screenHeight; //высота экрана
- protected final Graphics graphics; //объект для рисоваиния
- public Screen(){
- super(true);
- setFullScreenMode(true); //устанавливаем полный экран
- screenWidth=getWidth(); //получаем ширину экрана
- screenHeight=getHeight(); //получаем высоту экрана
- graphics=getGraphics(); //получаем объект для рисования
- }
- //метод, который вызывается первым после установки на дисплей
- public abstract void start();
- }
Мы унаследовали наш общий интерфейс от GameCanvas и внесли в него некоторые изменения:
Добавили собственные поля с модификатором protected и определили общее событие.
protected поля могут видеть только классы-потомки.
abstract в определении метода означает, что метод является абстрактным. То есть мы не пишем тело метода, но точно знаем, что потомки его реализуют (если только они не будут абстрактными). Если метод содержит как минимум один абстрактный метод, то класс должен быть помечен абстрактным, иначе компилятор выдаст ошибку.
Далее, создавая наши экраны, мы будет наследовать их уже от Screen, а не от GameCanvas. У нас получается следующая иерархия:
Object -> GameCanvas -> Screen -> НашЭкран
В Java все классы (даже неявно) наследуются от Object. Screen унаследован от GameCanvas, чтобы оставаться "холстом" для рисования и обработки нажатий (хотя это далеко не всё). Все наши экраны му будем наследовать от Screen для того, чтобы определить общий интерфейс. Было бы не совсем правильно писать одно и то же в каждом классе, тем более что общий интерфейс нам понадобится ещё для одной конструкции, о которой вы узнаете позже
Главный класс
- public class Main extends MIDlet{
- public static Main midlet; //ссылка на себя
- private final Display display;
- public Main(){
- midlet=this;
- display=Display.getDisplay(this); //получаем ссылку на дисплей
- }
- //установить экран на дисплей
- public void setCanvas(Screen canvas){
- display.setCurrent(canvas);
- canvas.start();
- }
- //...
- }
В главном классе определяем поля midlet и display. Первый ссылается на самого себя, а второй представляет собой ссылку на Display. На более подробном описании ссылки midlet мы остановимся чуть позже
Рассмотрим метод setCanvas подробнее: метод setCanvas принимает в параметре ссылку на любой класс, унаследованный от Screen, затем, так как Screen унаследован от GameCanvas, то устанавливает его на дисплей и с помощью метода start запускает его. Вся загвоздка в Screen - мы можем передать в метод экземпляр любого класса, унаследованного от Screen, будь то экран меню, или игры, а благодаря общему интерфейсу мы точно знаем, что у любого экземпляра есть реализованный метод start. Всё это осуществляется благодаря позднему связыванию в Java, если бы не оно, то для каждого класса-потомка 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); //выводим текст в координаты 0,0
- flushGraphics(); //перерисовываем графику на экране
- try{
- //засыпаем на 10 секунд
- Thread.sleep(10000);
- }catch(InterruptedException ie){
- }
- }
- }
Легко заметить, что в методе start мы уже пользуемся классами, которые инициализировали в нашем классе-предке.
Теперь пишем подобный класс для экрана игры:
- 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); //выводим текст в координаты 0,0
- flushGraphics(); //перерисовываем графику на экране
- while(true){
- //уходим в бесконечность 8)
- }
- }
- }
И добавляем ссылки на наши экраны в главный класс:
- public class Main extends MIDlet{
- public static Main midlet; //ссылка на себя
- private final Display display;
- public final Screen splash; //ссылка на экран "меню"
- public final Screen game; //ссылка на экран "игра"
- public Main(){
- midlet=this;
- display=Display.getDisplay(this); //получаем ссылку на дисплей
- game=new Game();
- splash=new Splash();
- }
- //установить экран на дисплей
- public void setCanvas(Screen canvas){
- display.setCurrent(canvas);
- canvas.start();
- }
- public void startApp(){
- setCanvas(splash); //устанавливаем первый экран на дисплей
- }
- }
Обратим внимание на ссылку midlet: она является статистической, публичной и указывает на экземпляр класса, в котором находится. Зачем это нужно? public обозначает, что к ссылке можно обратится в любом месте программы, static обозначает, что к ссылке можно обратится через класс (Main.midlet), а так как все поля экранов и метод setCanvas являются тоже публичными, следовательно, экран можно заменить в любом месте программы:
- Main.midlet.setCanvas(Main.midlet.game);
После первого прочтения новичок, наверное, не поймёт ни единого слова, однако при более вдумчивом чтении можно заметить всю простоту алгоритма и его логичное решение, используя технологию ООП. В статье я постарался описать детально всё, что необходимо, однако если у вас остались вопросы или неточности, то задавайте их в комментариях. Так же вы можете протестировать данный код, скачав полные исходники.
Полные исходники: MIDletStructure.zip