도움말을 제외한 나머지 서브 메뉴들은 모두, CyItemList라는 클래스를 포함하여 만듭니다.

CyItemList의 전체적인 구조는 CyMenu 클래스와 동일합니다.

다만, CyMenu 클래스의 경우, 메뉴 항목이 그림이었으나, CyItemList는 항목들이 문자열이라는 것이 다르죠.

* CyItemList.java

package common;

import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Graphics;

import com.skt.m.Graphics2D;

/**
 * 간단한 항목들을 표시할때 사용한다. 
 */

public class CyItemList
{
    /*
     * 게임에서 쓰일 상수.
     */
    private final int FONT_HEIGHT;  // 글자의 높이.

    private final byte FIRST;       // 제일 처음 항목의 인덱스.
    private final byte LAST;        // 제일 마지막 항목의 인덱스.

    private final byte TITLE_TOP=5; // 타이틀의 Y 좌표.

    // 스크롤 바의 타입.
    private final byte UPARROW = 0;
    private final byte DOWNARROW = 1;

    // 스크롤바의 폭과 높이.
    private final byte SCROLL_WIDTH;
    private final byte SCROLL_HEIGHT;

    /*
     * 일반 변수.
     */
    private String title;           // ItemList의 제목.
    private String[] itemName;      // 각 항목의 이름.
    private byte[] itemValue;      // 각 항목의 설정된 값.

    private byte maxItem;           // 항목의 갯수.

    private byte curItemIdx;        // 현재 선택된 항목 인덱스.
    private byte dispCnt;           // 화면에 보여줄 최대 항목 갯수.
    private byte minDispItemIdx;    // 현재 화면에 보여지는 최소 메뉴 인덱스.
    private byte maxDispItemIdx;    // 현재 화면에 보여지는 최대 메뉴 인덱스.

    private short borderLeft;       // ItemList의 경계선 Left.
    private short borderTop;        // ItemList의 경게선 Top.
    private short borderWidth;      // ItemList의 경계선 Width.
    private short borderHeight;     // ItemList의 경계선 Hidth.

    private byte itemTop;           // 첫번째 Item의 Top.
    private byte itemBoxLeft;       // 선택된 Item Box의 Left.
    private byte itemBoxTop;        // 선택된 Item Box의 Top.
    private byte itemBoxWidth;      // 선택된 Item Box의 Width.
    private byte itemBoxHeight;     // 선택된 Item Box의 Height.

    private Image sprScroll;        // 스크롤 화살표의 스프라이트.

    private short upScrollLeft;     // 위쪽 스크롤 화살표의 Left.
    private short upScrollTop;      // 위쪽 스크롤 화살표의 Top.

    private short downScrollLeft;   // 아래쪽 스크롤 화살표의 Left.
    private short downScrollTop;    // 아래쪽 스크롤 화살표의 Top.

    private byte itemInterval;      // 각 항목간의 간격.
    private byte defaultInterval;   // 기본 Interval.
    private byte cntAddedItem;      // 추가된 항목의 수.

    /*
     * 각 객체의 색상 변수.
     */
    private int colorOfTitleText;   // 타이틀의 글자 색.
    private int colorOfItemText;    // 각 항목의 글자 색.
    private int colorOfItemBoxText; // 선택된 항목의 글자 색.

    private int colorOfTitleBg;     // 타이틀의 배경 색.
    private int colorOfBorderBg;    // Border의 배경 색.
    private int colorOfItemBg;      // 각 항목의 배경 색.
    private int colorOfItemBoxBg;   // 선택된 항목의 배경 색.

    private Canvas parent;          // ItemList를 그려줄 캔버스.

    int width;
    int height;

    /**
     * Constructor.
     * @param maxItem
     */
    public CyItemList(Canvas parent, int maxItem)
    {
        this.parent = parent;

        // 스크롤 바의 이미지를 읽어 옴.
        try {
            sprScroll = loadImage("/image/menu/SprScroll.lbm");
        } catch(Exception e) {}

        // 스크롤 바의 크기 저장.
        SCROLL_WIDTH = (byte)(sprScroll.getWidth()/2);
        SCROLL_HEIGHT = (byte)sprScroll.getHeight();

        // 폰트 높이를 구해온다.
        FONT_HEIGHT = (byte)((Font.getDefaultFont()).getHeight());

        /*
         * Default 색상 설정.
         */
        colorOfTitleBg = 0x000090;
        colorOfItemBg = 0x500000;
        colorOfItemBoxBg = 0x007800;

        colorOfTitleText = 0xffffff;
        colorOfItemText = 0xffffff;
        colorOfItemBoxText = 0xffff00;

        curItemIdx = 0;                  // 현재 선택된 항목의 인덱스 초기화.
        cntAddedItem = 0;                // 추가된 항목의 갯수 초기화.

        this.maxItem = (byte)maxItem;   // 항목의 갯수 초기화.

        // 항목이 없는 ItemList는,
        if(maxItem == 0)
        {
            FIRST = 0;
            LAST = 0;

            setBorderMaxSize(); // Border 크기를 최대로 설정한다.
        }
        else // 항목이 1개 이상이라면,
        {
            FIRST = 0;                       // 첫번째 항목의 인덱스.
            LAST = (byte)(maxItem - 1);     // 마지막 항목의 인덱스.

            itemInterval = 10;               // 항목 사이의 간격.
            defaultInterval = 5;             // 기본 간격 설정.

            itemName = new String[maxItem]; // 항목 이름을 저장할 배열 생성.
        }

        width = parent.getWidth();          // 캔버스 Width.    
        height = parent.getHeight()+16;     // 캔버스 Height.
    }

    /**
     * 이미지를 읽어온다.
     * @param path
     * @return
     * @throws Exception
     */
    private Image loadImage(String path) throws Exception
    {
        Image img = null;

        try
        {
            img = Image.createImage(path);
        } catch(Exception e)
        {
            throw e;
        }

        return img;
    }

    /**
     * 화면에 ItemList를 그려줌.
     * @param g
     */
    public void draw(Graphics g)
    {
        drawObj(g);
    }

    /**
     * 아이템, 요소, 제목등을 화면에 표시
     * @param g 표시할 graphic 개체
     */
    public void drawObj(Graphics g)
    {
        drawTitle(g);
        drawBorder(g);

        // 메뉴 항목이 1개 이상 존재할때,
        if(maxItem != 0) // 메뉴 항목을 그려준다.
            drawItem(g);
    } // end public void drawObj(Graphics g, int baseX, int baseY).

    /**
     * Border를 그려 줌.
     */
    private void drawBorder(Graphics g)
    {
        // Border의 내부 배경색을 칠해줌.
        g.setColor(colorOfBorderBg);
        g.fillRect(borderLeft, borderTop, borderWidth, borderHeight);

        // Border의 외곽선을 그려줌.
        g.setColor(0xffffff);
        g.drawRect(borderLeft, borderTop, borderWidth, borderHeight);
    }

    /**
     * 타이틀을 그려줌.
     * @param g
     */
    private void drawTitle(Graphics g)
    {
        g.setColor(colorOfTitleBg);
        g.fillRect(0, TITLE_TOP, width, FONT_HEIGHT);
        g.setColor(colorOfTitleText);
        g.drawString(title, width/2, TITLE_TOP, Graphics.HCENTER|Graphics.TOP);
    }

    /**
     * 메뉴 항목을 그려 줌.
     * @param g
     */
    private void drawItem(Graphics g)
    {
        for(int i=minDispItemIdx; i<=maxDispItemIdx; i++)
        {
            int indexTop = (i-minDispItemIdx)*(FONT_HEIGHT+itemInterval);

            if(i == curItemIdx) // 현재 선택된 항목이라면,
            {
                // 선택 박스 출력 후,
                g.setColor(colorOfItemBoxBg);
                g.fillRect(itemBoxLeft,itemBoxTop+indexTop,
itemBoxWidth,itemBoxHeight); // 글씨 출력. g.setColor(colorOfItemBoxText); // 선택되었을 때의 글씨색. g.drawString(itemName[i], width/2, itemTop + indexTop,
Graphics.HCENTER|Graphics.TOP); } else
// 현재 선택된 항목이 아니라면, { // 글씨만 출력. g.setColor(colorOfItemText); // 선택이 안된 때의 글씨색. g.drawString(itemName[i], width/2, itemTop + indexTop,
Graphics.HCENTER|Graphics.TOP); } } /*
* 스크롤 바를 그려준다. */ // 화면에 보이는 최저 인덱스가, if(minDispItemIdx != FIRST) // 처음이 아니라면, { // 위쪽 화살표를 그려준다. drawScroll(g, UPARROW); } // 화면에 보이는 최대 인덱스가, if(maxDispItemIdx != LAST) // 마지막이 아니라면, { // 아래쪽 화살표를 그려준다. drawScroll(g, DOWNARROW); } } /** * 스크롤 바를 그려준다. * @param g * @param scrollType */ private void drawScroll(Graphics g, int scrollType) { Graphics2D g2d = Graphics2D.getGraphics2D(g); if(scrollType == UPARROW) // 위쪽 화살표라면, { g2d.drawImage(upScrollLeft, upScrollTop, sprScroll, 0, 0, SCROLL_WIDTH,
SCROLL_HEIGHT, Graphics2D.DRAW_COPY); } else
/* if(scrollType == DOWNARROW) */ { g2d.drawImage(downScrollLeft, downScrollTop, sprScroll, SCROLL_WIDTH,
0, SCROLL_WIDTH, SCROLL_HEIGHT, Graphics2D.DRAW_COPY); } } /**
* 타이틀 제목 설정. * @param titleStr */ public void setTitle(String titleStr) { title = titleStr; } /** * 항목 추가. * @param itemStr */ public void addItem(String itemStr) { // 항목 이름 대입. itemName[cntAddedItem] = itemStr; if(cntAddedItem == LAST) { setBorderSize(); setItemBoxSize(); System.out.println("마지막 항목 입력."); } cntAddedItem++; } /** * 스크롤 바의 위치 설정. */ private void setScrollPos() { // 위쪽 화살표의 위치 설정. upScrollLeft = (short)(borderLeft + borderWidth - SCROLL_WIDTH); upScrollTop = (short)(borderTop + SCROLL_HEIGHT); // 아랫쪽 화살표의 위치 설정. downScrollLeft = upScrollLeft; downScrollTop = (short)(borderTop+borderHeight
-defaultInterval-SCROLL_HEIGHT); } /**
* Border의 위치를 설정. * @param x * @param y */ private void setBorderPos(int x, int y) { borderLeft = (byte)x; borderTop = (byte)y; setScrollPos(); // 스크롤 바의 위치를 설정한다. } /** * Item List의 객체들을 가운데 정렬 한다. * @param bottomCanon 아래쪽의 어디까지 정렬 할 것인가. */ public void alignObjectCenter(int bottomCanon) { // Border 가운데 정렬. setBorderPos((width - borderWidth)/2, (TITLE_TOP+(TITLE_TOP+FONT_HEIGHT+bottomCanon)-borderHeight)/2); // 선택 사각형 가운데 정렬. setItemBoxPos((width-itemBoxWidth)/2+1,
borderTop+itemInterval+defaultInterval); } /**
* Border의 크기 설정. */ private void setBorderSize() { borderWidth = 106; borderHeight = (short)((FONT_HEIGHT+itemInterval)
* dispCnt + itemInterval + defaultInterval*2); } /**
* Border의 크기를 최대로 설정. */ private void setBorderMaxSize() { borderWidth = 106; borderHeight = 108; } /** * 첫번째 항목이 선택되었을때의 박스 위치 설정. * @param x * @param y */ private void setItemBoxPos(int x, int y) { // 첫번째 항목의 위치 설정. itemTop = (byte)y; // 첫번째 항목이 선택되었을때의 박스 위치 설정. itemBoxLeft = (byte)x; itemBoxTop = (byte)y; } /** * 첫번째 항목이 선택되었을때의 박스 크기 설정. */ private void setItemBoxSize() { // 선택 사각형의 폭과 높이 설정. itemBoxWidth = (byte)(borderWidth - 7); itemBoxHeight = (byte)FONT_HEIGHT; } /** * 현재 선택된 항목의 인덱스를 리턴. * @return */ public byte getCurItemIdx() { return curItemIdx; } /** * 화면에 보여줄 항목 갯수를 설정. * @param dispCnt 한 화면에 보여질 항목의 갯수. */ public void setDispCnt(int dispCnt) { this.dispCnt = (byte)dispCnt; // 한 화면에 보여줄 항목 갯수. // 화면에 보이는 최소 인덱스. minDispItemIdx = curItemIdx; // 화면에 보이는 최대 인덱스. maxDispItemIdx = (byte)(curItemIdx + dispCnt - 1); System.out.println(" ( " + minDispItemIdx + ", " + maxDispItemIdx + ")"); } /** * 각 항목간의 간격을 설정한다. * @param interval */ public void setItemInterval(int interval) { itemInterval = (byte)interval; } /** * 타이틀의 배경색 설정. * @param colorCode */ public void setColorOfTitleBg(int colorCode) { colorOfTitleBg = colorCode; } /** * Border의 배경색 설정. * @param colorCode */ public void setColorOfBorderBg(int colorCode) { colorOfBorderBg = colorCode; } /** * 선택된 항목의 배경색 설정. * @param colorCode */ public void setColorOfItemBoxBg(int colorCode) { colorOfItemBoxBg = colorCode; } /** * 이전 항목으로 포커스 이동. */ public void pre() { /* 현재 포커스가 가장 처음이라면, */ if(curItemIdx == FIRST) return; /* 현재 보이는 최소 인덱스가 처음이 아니라면, */ if(curItemIdx == minDispItemIdx) { minDispItemIdx--; maxDispItemIdx--; } curItemIdx--; // 현재 선택된 인덱스 +1 } /** * 다음 항목으로 포커스 이동. */ public void next() { /* 현재 포커스가 가장 마지막이라면, */ if(curItemIdx == LAST) return;
/* 현재 보이는 최대 인덱스가 마지막이 아니라면, */
if(curItemIdx == maxDispItemIdx) { minDispItemIdx++; maxDispItemIdx++; } curItemIdx++; // 현재 선택된 인덱스 +1. } }


소스가 조금 기네요.

그러나 대부분 인터페이스를 꾸미는데 필요한 코드 들일뿐, 실제 중요한 코드는 몇줄 되지 않습니다.

또 말씀 드렸다시피, CyMenu 클래스의 구조와 거의 흡사 합니다.

이해를 돕기 위해, 잠깐 CyItemList를 이용해서 만든 메뉴의 예를 한번 보도록 하죠.


구조가 거의 메인 메뉴와 비슷합니다. 그렇죠?

일단 차이점이라면, 먼저 이 화면이 뭘 하는 Item List인지 표시해 주는 타이틀이 있고,


스크롤바가 가운데에 있지 않고, 오른쪽에 있네요.

그리고 가장 큰 차이점은, 항목이 그림이 아니라 String 이라는 것...

그 외에는 모두 동일 합니다.




먼저, 생성자를 보면, 스크롤 바로 사용될 이미지를 불러오고, 스크롤 바의 크기를 구해서 상수를 초기화 시킵니다.

그리고 그다음에 폰트의 높이를 구하는 부분이 있는데요.


// 폰트 높이를 구해온다.
FONT_HEIGHT = (byte)((Font.getDefaultFont()).getHeight());

Font 클래스의 static 매소드인 getDefaultFont()를 이용해서 기본 글꼴의 정보를 얻어 온후,

getHeight() 함수를 이용해서 기본 글꼴의 높이를 얻어 왔습니다.


그리고 다음 부분은, 각각의 객체의 색깔을 지정해 주는 부분인데요.

/*
* Default 색상 설정.
*/
colorOfTitleBg = 0x000090;
colorOfItemBg = 0x500000;
colorOfItemBoxBg = 0x007800;
colorOfTitleText = 0xffffff;
colorOfItemText = 0xffffff;
colorOfItemBoxText = 0xffff00;

여태까지 우리는 g.setColor(255, 255, 255)와 같이 g객체를 통해서 색깔을 지정해 주었지만,

위에서는 직접 #ffffff와 같이 컬러 코드를 지정해 주고 있습니다.

#ffffff 와 같은 코드는, 앞에서부터 두자리씩 각각의 색상 코드를 나타냅니다.

즉, #RrGgBb와 같은 형식이죠.

앞에 두자리인 FF는 Red에 해당하는 16진수이고, 두번째 두자리는 Green에 해당하는 16진수, 세번째는 Blue...


FF는 10진수로 변환하면 255와 같으므로, 위의 #ffffff 는, 즉 RGB 코드로 (255, 255, 255)와 같은 의미이겠지요.

위와 같이 직접 컬로 코드로 정의해 놓으면, g.setColor(colorOfTitleBg)와 같이,

setColor()의 해당 컬러 코드를 인자로 넘겨주면 색상이 변경이 됩니다.


그외의 나머지 함수들은 전부다 외부 혹은 내부에서 클래스의 변수 값을 변경하기 위한 함수들입니다.

setXXXXXPos() 와 같은 함수는 XXXX라는 객체의 위치를 변경하는 함수를 의미하고,

setXXXXXSize() 와 같은 함수는 XXXX라는 객체의 위치를 변경하는 함수.... 뭐 이런 식이죠.

get의 경우에는 값을 얻어올때 사용하는 함수가 되겠죠.


그리고 마찬가지로, CyItemList 클래스에도 pre()와 next()가 있습니다.

pre()/next()의 코드 내용 자체는 CyMenu의 pre()/next()와 동일 합니다.

전혀 다를게 없죠.


이제, 이걸로 CyItemList 클래스도 살펴 보았습니다.

이제 CyItemList 클래스를 이용해서 ItemList를 출력해 주면 되겠네요.

그런데 메뉴 항목중에서 게임 시작과 도움말을 뺀 나머지 서브 메뉴들은 모두 CyItemList 클래스를 사용합니다.

결국... 나머지 서브메뉴들의 구조가 모두 동일하겠죠. 같은 클래스를 사용했으니까요.

실제로 저도 나머지 서브 메뉴들은, 가장 먼저 겔러리를 만든 후,

나머지는 모두 복사해서 클래스 이름만 변경해서 작성했습니다.


함수 이름이나 클래스 구조가 완전히 동일하므로, 여기서는, 겔러리 소스 만을 분석해 보도록 하겠습니다.


* Gallery.java
package twogo1p;

import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Graphics;
import common.CyItemList;

/**
 *      겔러리.
 */
class Gallery extends Canvas {

    private Midlet midlet;
    private CyItemList itemList;
    private short width, height;
    private Canvas callBackDisplayCanvas;

    /**
     * Constructor.
     * @param midlet
     */
    Gallery(Midlet midlet)
    {
        this.midlet = midlet;
        width = (short) getWidth();
        height = (short) (getHeight()+16);

        itemList = new CyItemList(this, 11);

        itemList.addItem("그림 1");
        itemList.addItem("그림 2");
        itemList.addItem("그림 3");
        itemList.addItem("그림 4");
        itemList.addItem("그림 5");
        itemList.addItem("그림 6");
        itemList.addItem("그림 7");
        itemList.addItem("그림 8");
        itemList.addItem("그림 9");
        itemList.addItem("그림 10");
        itemList.addItem("돌아가기");
		
        itemList.setDispCnt(6);
        itemList.setTitle("겔러리");
        itemList.setItemInterval(0);

        itemList.setColorOfTitleBg(0x0000A0);
        itemList.setColorOfBorderBg(0x990000);
        itemList.setColorOfItemBoxBg(0x997700);
		
		   // ItemList를 가운데 정렬 한다.
        itemList.alignObjectCenter(height-16*2);
    }

    /**
     * 이전 Canvas를 저장한다.
     * @param canvas
     */
    public void savePreCanvas(Canvas canvas)
    {
        this.callBackDisplayCanvas = canvas;
    }


    /**
     * 화면에 겔러리 화면을 그려준다.
     */
    protected void paint(Graphics g)
    {
        g.setColor(0,0,0);
        g.fillRect(0,0,width,height);

        itemList.draw(g);
    }

    /**
     * 전체화면 refresh.
     */
    private void repaints()
    {
        repaint(0, 0, width, height);
    }

    /**
     * 키 입력 처리.
     */
    public void keyPressed(int keyCode)
    {
        switch(keyCode)
        {
        case KEY_UP:
            itemList.pre();
            repaints();
            break;

        case KEY_DOWN:
            itemList.next();
            repaints();
            break;

        case KEY_CLR:
            returnToPreCanvas();
            break;

        case KEY_FIRE:
        case KEY_COMR:

            if(itemList.getCurItemIdx() == 10)
            {
                returnToPreCanvas();
            }
            break;

        }
    }


    /**
     * 이전 Canvas 화면을 보여준다.
     */
    private void returnToPreCanvas()
    {
        midlet.setCurrentDisplay(callBackDisplayCanvas);
        midlet = null;
        callBackDisplayCanvas = null;
        itemList = null;
        System.gc();
    }

}

먼저 생성자를 보면, ItemList의 여러 속성들을 초기화 시켜주고 있습니다.


itemList = new CyItemList(this, 11);
itemList.setDispCnt(6);
itemList.setTitle("겔러리");
itemList.setItemInterval(0);


CyItemList 객체를 생성할때 준 두번째의 인자가 11이므로, 총 11개의 항목을 생성할 수 있습니다.

그래서, 그림 1번 부터 10번까지와, 돌아가기 항목을 addItem() 함수로 생성해 주었습니다.


그리고 그다음 setDispCnt() 함수로, 한 화면에 총 6개의 항목씩 출력 되도록 DispCnt를 초기화 해주고,

Title 제목은 "겔러리" 라고 초기화 해 주었으며, 각 항목 사이의 간격은 0으로 초기화 해주었습니다.

그리고 다음으로 alignObjectCenter() 함수를 이용해서, ItemList 객체들을 화면에 가운데 정렬하여 보여줍니다.

항목을 입력 받은 뒤에 가운데 정렬을 하는 것은 항목의 갯수에 따라 ItemList의 사이즈가 다르기 때문입니다.


그리고 나머지 함수들은 예전에 이미 구현했던 내용과 동일합니다.

키 입력 처리 부분 역시 마찬가지로 메인 메뉴에서 구현했던 내용과 완전 동일 합니다.

위쪽 화살표가 입력되면, pre() 함수로 이전 항목을 선택하고,

아래쪽 화살표가 입력되면, next() 함수로 당므 항목을 선택합니다.


그런데, 사실 겔러리는 이게 다는 아닙니다.

겔러리는 원래 게임에서 번 돈으로 사진을 사서 감상할수 있는 공간인데,

아직 우리가 게임을 구현하지 않았으므로, 게임이 완벽하게 구현 된 뒤에, 겔러리를 완성 시키도록 하겠습니다.

환경 설정 부분도, 사운드와 진동을 설정해 주어야 하는데, 아직 우리가 사운드 클래스를 구현하지 않았으므로,

사운드 클래스를 제작한 후에, 다시 완성하도록 하겠

Posted by maysent
: