반응형
블로그 이미지
개발자로서 현장에서 일하면서 새로 접하는 기술들이나 알게된 정보 등을 정리하기 위한 블로그입니다. 운 좋게 미국에서 큰 회사들의 프로젝트에서 컬설턴트로 일하고 있어서 새로운 기술들을 접할 기회가 많이 있습니다. 미국의 IT 프로젝트에서 사용되는 툴들에 대해 많은 분들과 정보를 공유하고 싶습니다.
솔웅

최근에 올라온 글

최근에 달린 댓글

최근에 받은 트랙백

글 보관함

카테고리


반응형
어제까지 잘 실행해 보셨나요?

저의 경우엔 textfield 하고 textarea 표시할 때 label (Title, Narrative) 가 안나오더라구요.
그리고 상단 툴바에 home 버튼만 나오고 save 버튼은 안나오구요.
그리고 하단 버튼에 trash 버튼도 안 나오고......


원래는 이렇게 나와야 되는데 아래처럼 나오네요.


저 왼쪽 스크롤을 내리면은 아래 툴바가 나오긴 하는데 trash 버튼은 안 나옵니다.

브라우저 버전일까 싶어서 크롬 최신버전으로 깔아도 똑 같은 문제네요.
이 소스가 원래 Sencha Touch 1.1.1 버전을 기반으로 한 거라서 따로 인터넷 검색해서 1.1.1 버전의 css 하고 js 파일을 다운 받아 썼는데... 그 파일 버전이 잘못 된건지......

여러분은 어떻게 나오시나요?

일단 오늘 Sencha Touch 의 Notes Application 마지막 부분을 정리하겠습니다.
잘 안되는 부분은 다시 해결해 봐야겠습니다.
(맨 아래 최종버전 압축파일 올리겠습니다.)
혹시 원래 설계대로 잘 되시는 분 있으면 연락 주세요.

===== o ===== o ===== o ===== o ===== o =====

이번 강좌가 Sencha Touch 로 첫번째 앱 만들기 마지막 시간입니다.
지난 시간에는 Note Editor 와 새로운 Notes를 생성하는 기능을 구현해 봤습니다.
이제 수정하고 delete하는 기능을 살펴 보겠습니다.

***** Disclosure events in a Sencha Touch List

유저가 이 앱을 사용하면서 disclosure 버튼(화살표 버튼)을 누르게 되면 해당 Note의 제목과 내용이 보여지게 됩니다.



우리는 이 기능을 notes list의 onItemDisclosure 핸들러를 통해 구현할 수 있습니다.

NotesApp.views.notesList = new Ext.List({
    id: 'notesList',
    store: 'NotesStore',
    onItemDisclosure: function (record) {
        var selectedNote = record;
        NotesApp.views.noteEditor.load(selectedNote);
        NotesApp.views.viewport.setActiveItem('noteEditor', { type: 'slide', direction: 'left' });
    },
    itemTpl: '
<div class="list-item-title">{title}</div>' +
        '<div class="list-item-narrative">{narrative}</div>',
    listeners: {
        'render': function (thisComponent) {
            thisComponent.getStore().load();
        }
    }
});

이 핸들러 함수는 파라미터로서 선택된 note를 받습니다. 이 핸들러에서 우리가 구현해야 할 것은 에디터의 load()메소드를 사용해서 note를 load 하는 겁니다.
그리고 나서 viewport의 setActiveItem()을 호출해서 Note Editor의 view를 active 시킵니다.

***** Removing records from a data store

notes를 지우는 기능도 아주 간단히 구현할 수 있습니다.
화면 아래 툴바에 있는 휴지통 버튼이 이 기능을 실행하도록 할 겁니다.
아래처럼 이 버튼 핸들러를 수정합니다.

NotesApp.views.noteEditorBottomToolbar = new Ext.Toolbar({
    dock: 'bottom',
    items: [
        { xtype: 'spacer' },
        {
            iconCls: 'trash',
            iconMask: true,
            handler: function () {

                var currentNote = NotesApp.views.noteEditor.getRecord();
                var notesList = NotesApp.views.notesList;
                var notesStore = notesList.getStore();

                if (notesStore.findRecord('id', currentNote.data.id)) {
                    notesStore.remove(currentNote);
                }

                notesStore.sync();

                notesList.refresh();
                NotesApp.views.viewport.setActiveItem('notesListContainer', { type: 'slide', direction:

'right' });
            }
        }
    ]
});

현재의 note에 대한 reference 정보들을 확보한 다음에 우리는 store의 findRecord() 함수를 사용해 에디터에 로드된 note를 찾아서 지울겁니다.
그리고 나서 sync()를 호출해서 이 삭제작업에 대해 commit 합니다. 그리고 다시 리스트를 render 해서 새로운 Notes List view를 보여줍니다.

이 기능은 Save Note 부분에서 처리됐던 과정이랑 비슷합니다. 단지 saving 이 아니라 deleteing 만 달르 뿐입니다.

***** Grouping items in a Sencha Touch List

이제 마지막으로 남은것은 Notes List view 에서 날짜별로 그루핑 하는 겁니다. 유저가 note들을 날짜별로 구분해서 보면 훨씬 더 보기 좋을테니까요.

첫번째로 Ext.List에게 아이템들이 group화 되어야 한다고 알려줘야 합니다. 이것은 grouped config 옵션을 사용해서 구현할 수 있습니다.

NotesApp.views.notesList = new Ext.List({
    id: 'notesList',
    store: 'NotesStore',
    grouped: true,
    emptyText: '<div style="margin: 5px;">No notes cached.</div>',
    onItemDisclosure: function (record) {
        var selectedNote = record;
        NotesApp.views.noteEditor.load(selectedNote);
        NotesApp.views.viewport.setActiveItem('noteEditor', { type: 'slide', direction: 'left' });
    },
    itemTpl: '<div class="list-item-title">{title}</div>' +
        '<div class="list-item-narrative">{narrative}</div>',
    listeners: {
        'render': function (thisComponent) {
            thisComponent.getStore().load();
        }
    }
});

그 다음엔 NotesStore의 getGroupStrint() 함수를 오버라이드 해야 합니다.

Ext.regStore('NotesStore', {
    model: 'Note',
    sorters: [{
        property: 'date',
        direction: 'DESC'
    }],
    proxy: {
        type: 'localstorage',
        id: 'notes-app-store'
    },
    getGroupString: function (record) {
        if (record && record.data.date) {
            return record.get('date').toDateString();
        } else {
            return '';
        }
    }
});

이제 groupField 프로퍼티와 getGroupString() 함수를 이용해서 구체적인 사항들을 정의하실 수 있습니다.

getGroupString() 함수는 store의 data model 프로퍼티에 근거한 string을 리턴합니다.
이 앱의 경우는 그룹의 헤더로 note의 date 값을 사용할 겁니다.
에뮬레이터로 보면 아래와 같이 나올 겁니다.



***** We made it!


이제 완성했습니다. 아주 간단한 Sencha Touch 애플리케이션이죠? 그래서 Sencha Touch 프레임워크에 대해 쉽게 이해할 수 있을 것 같습니다.
이제 첫번째 앱을 만들면서 감을 잡았으니 Sencha Touch로 된 좀 더 복잡한 애플리케이션을 만들 수 있을 겁니다.

아래 지금까지 진행한 소스가 있습니다.
다운받아서 실행해 보세요.


이걸 실행해보면 처음에 얘기했던대로 제 브라우저에서는 label 도 안나오고 save 버튼하고 trash 버튼 모두 안나오네요.

여러분은 제대로 나오시는지 궁금하네요.
sencha-touch.css 하고 sencha-touch-debug.js 도 다른 버전으로 바꿔보고 브라우저도 여러군데서 실험해 봐야겠습니다.

성공하신분 댓글로 정보 부탁드립니다.

다음엔 Sencha Touch 최신버전 (2.0) 에 맞는 예제소스로 공부해 볼 생각입니다.

추천추천 부탁드려요..~~~~~~~~~~~
반응형


반응형
지난 시간에는 New 버튼을 달고 더미 데이터를 넣어서 List 를 출력해 봤습니다.
잘 되셨나요?

지난주까지 진행한 소스코드 아래에 있습니다.
필요하신 분은 받아서 보세요.


오늘은 Note Editor 부분을 하겠습니다.
Notes List 에서 Note Editor 화면으로 전환하고 새 데이터를 넣고 저장하고 이것을 List 로 새로 출력하는 기능을 할 겁니다.
이 과정에서 validation도 체크하는 기능도 다룰거구요.

아래 오늘의 내용을 보시죠.

Writing a Sencha Touch Application, Part 3

Part 1,2 에서는 Notes 앱 중에 Notes List view 부분을 다뤘습니다. 오늘은 Note Editor view를 다루겠습니다.
타이틀은 Edit Note 가 될 텐데요. Note의 생성, update, 삭제 기능이 있을 겁니다.
아래 Mock up 이미지와 완성 화면이 있습니다.








***** Creating a Sencha Touch Form Panel

Note Editor mockup을 보면 우리는 툴바와 필드들을 만들어야 합니다.
우선 form field들 부터 만들겠습니다.

NotesApp.views.noteEditor = new Ext.form.FormPanel({
    id: 'noteEditor',
    items: [
        {
            xtype: 'textfield',
            name: 'title',
            label: 'Title',
            required: true
        },
        {
            xtype: 'textareafield',
            name: 'narrative',
            label: 'Narrative'
        }
    ]
});


이 필드들을 만들기 위해 Ext.form.FormPanel 클래스를 사용했습니다. 센차터치에서 form 필드들을 만들 때 가장 손쉽게 만들 수 있는 방법입니다.
제목은 textfield를 사용하고 내용을 넣는 공간은 Text field를 사용합니다.
title 부분에 required 를 true로 했습니다. 반드시 이곳에 값을 넣어야 한다는 겁니다. 디비의 not null 이죠. 나중에 save 하는 단계에서 이곳에 값을 넣지 않으면 어떻게 되는지 보실 수 있습니다.

툴바를 넣기 전에 이 form들의 모습이 어떤지 미리 보면 좋겠죠? viewport 부분을 약간 수정해서 이 form이 보이도록 해 보죠.
viewport의 items 부분을 수정해 아래처럼 수정해 보세요. 그러면 지난번에 만들었던 list가 아니라 edit form이 나올겁니다.

NotesApp.views.viewport = new Ext.Panel({
    fullscreen: true,
    layout: 'card',
    cardAnimation: 'slide',
    items: [NotesApp.views.noteEditor] // TODO: revert to [NotesApp.views.notesListContainer]
});

제대로 됐다면 아래처럼 보일겁니다.




***** Adding top and bottom toolbars to a Sencha Touch Panel

이제 툴바를 만들 차례입니다.
첫번째 툴바는 Home 과 Save 버튼이 있는 위쪽 툴바입니다.

NotesApp.views.noteEditorTopToolbar = new Ext.Toolbar({
    title: 'Edit Note',
    items: [
        {
            text: 'Home',
            ui: 'back',
            handler: function () {
                // TODO: Transition to the notes list view.
            }
        },
        { xtype: 'spacer' },
        {
            text: 'Save',
            ui: 'action',
            handler: function () {
                // TODO: Save current note.
            }
        }
    ]
});

위 소스 콛를 보시면 툴바를 만들면서 ui config 옵션을 사용하신 걸 보실 수 있을 겁니다. (back, action 으로 정의했죠?) home 버튼에는 back 이라고 정의했습니다. 왜냐하면 이 버튼을 누르면 back 해서 main view 로 갈 것이기 때문입니다. 그리고 save 버튼에는 action이라고 정의했는데요. 이것은 유저가  이 버튼을 누르면 note를 save 할 거라는 겁니다. 이 기능이 가장 중요한 기능 중 하나죠?



화면 아래에 있는 툴바에는 휴지통 버튼이 들어갈 겁니다. 이 버튼을 누르면 해당 note 가 delete 되겠죠?
NotesApp.views.noteEditorBottomToolbar = new Ext.Toolbar({
    dock: 'bottom',
    items: [
        { xtype: 'spacer' },
        {
            iconCls: 'trash',
            iconMask: true,
            handler: function () {
                // TODO: Delete current note.
            }
        }
    ]
});

여기에 몇가지 주의하실 부분이 있습니다. 첫번째로 dock config를 어떻게 사용했는지 보세요. 화면 아래에 배치하기 위해 bottom을 사용했습니다. 그리고 iconCls와 iconMask config 옵션을 보세요. 이 기능들은 버튼 안에 trash icon을 render 하기 위한 옵션들입니다.



이제 툴바를 add 하실 수 있습니다.

NotesApp.views.noteEditor = new Ext.form.FormPanel({
    id: 'noteEditor',
    items: [
        {
            xtype: 'textfield',
            name: 'title',
            label: 'Title',
            required: true
        },
        {
            xtype: 'textareafield',
            name: 'narrative',
            label: 'Narrative'
        }
    ],
    dockedItems: [
            NotesApp.views.noteEditorTopToolbar,
            NotesApp.views.noteEditorBottomToolbar
        ]
});

약간 코드를 수정해서 지금까지 한 작업을 화면으로 볼 수 있도록 해 보죠.

NotesApp.views.viewport = new Ext.Panel({
    fullscreen: true,
    layout: 'card',
    cardAnimation: 'slide',
    items: [NotesApp.views.noteEditor] // TODO: revert to [NotesApp.views.notesListContainer]
});

성공했으면 아래와 같은 화면을 보실 수 있을 겁니다.




아주 훌륭하죠?

지금까지 우리는 Notes List와 Note Editor 뷰를 만들었습니다. 이제 필요한 것은 이 앱의 workflow 입니다. 뷰 이외에 다른 비지니스 로직을 어떻게 구현하느냐를 말하는 것이죠. 우선 첫번째 기억할 비지니스 로직(Navigate screen) 은 Notes List 에서 New 버튼을 누르면 Notes Edit View 로 간다는 겁니다.



****** How to change views in a Sencha Touch application

이제 Notes List view 로 가 볼까요? 이제 New 버튼의 handler 부분을 다룰 차례입니다. 이 버튼이 눌려지면 새로운 note를 만들 수 있어야 합니다. 그러니까 이 버튼을 누르면 Note Editor를 화면에 보이도록 하면 되곘죠.

NotesApp.views.notesListToolbar = new Ext.Toolbar({
    id: 'notesListToolbar',
    title: 'My Notes',
    layout: 'hbox',
    items: [
        { xtype: 'spacer' },
        {
            id: 'newNoteButton',
            text: 'New',
            ui: 'action',
            handler: function () {

                var now = new Date();
                var noteId = now.getTime();
                var note = Ext.ModelMgr.create(
                    { id: noteId, date: now, title: '', narrative: '' },
                    'Note'
                );

                NotesApp.views.noteEditor.load(note);
                NotesApp.views.viewport.setActiveItem('noteEditor', {type: 'slide', direction: 'left'});
            }
        }
    ]
});

순서대로 한번 볼까요?
첫번째로 ModelMgr 클래스의 create()메소드를 사용해서 새로운 note를 생성합니다.

var now = new Date();
var noteId = now.getTime();
var note = Ext.ModelMgr.create(
    { id: noteId, date: now, title: '', narrative: '' },
    'Note'
);

그리고 나서 이 새 note를 Note Editor에 전해줍니다. FormPanel에서 이 부분이 보이도록 하기 위해 load 메소드를 사용했습니다. 이 load 메소드는 필드들을 보여줄겁니다. 이때 value 값들이 있으면 그 value 값들도 보여 줄 거구요.

NotesApp.views.noteEditor.load(note);

다음으로는 우리가 viewport에서 card layout을 사용했듯이 viewport이 setActiveItem()메소드를 사용해서 Note Editor를 보이도록 할 겁니다.
이 setActiveItem()을 call 하면 우리는 우리가 active 시키려고 하는 card의 id를 전달해 주고 그 객체가 transition 하는 animation 기능을 사용할 수 있습니다.

NotesApp.views.viewport.setActiveItem('noteEditor', {type: 'slide', direction: 'left'});

이 New 버튼이 어떻게 작동하는지 확인하기 전에 viewport의 items array를 약간 수정해야 합니다.

NotesApp.views.viewport = new Ext.Panel({
    fullscreen: true,
    layout: 'card',
    cardAnimation: 'slide',
    items: [
        NotesApp.views.notesListContainer,
        NotesApp.views.noteEditor
    ]
});



여기까지 완성 됐나요? 그러면 이제 user가 작성한 note를 save 할 수 있도록 하시면 됩니다.
이 기능은 당연히 Note Editor의 Save 버튼에 달아야 겠죠.

***** Validating a data model in Sencha Touch

save 버튼을 누르게 되면 우선 체크해야 할 것들이 몇가지 있습니다.

1. form field의 title과 narrative  정보는 Note Model 인스턴스에서 capture 됩니다.
2. note의 title의 value 가 없으면 유저에게 alert 화면을 띄울 겁니다.
3. note 가 new일 경우 이 note를 cache에 add 할 겁니다. 이 note 가 이전에 저장 됐던 것이면 cache에 있는 해당 note를 update 할 겁니다.
4. 그리고 나서 Notes List 를 refresh 해서 보여줄 겁니다.

자 이 기능들을 구현해 볼까요?
save 버튼의 tap 핸들러를 구현하기 전에 Note data model을 수정해서 validation 부분을 구현하는데 좀 더 편하도록 해 봅시다. 우리가 할 것은 model의 title 필드의 validation 함수를 사용해서 message 프로퍼티를 override 할 겁니다. 이 기능은 이 필드가 invalid 할 경우 메세지를 띄우는 기능입니다.

Ext.regModel('Note', {
    idProperty: 'id',
    fields: [
        { name: 'id', type: 'int' },
        { name: 'date', type: 'date', dateFormat: 'c' },
        { name: 'title', type: 'string' },
        { name: 'narrative', type: 'string' }
    ],
    validations: [
        { type: 'presence', field: 'id' },
        { type: 'presence', field: 'title', message: 'Please enter a title for this note.' }
    ]
});

이 메세지는 우리가 Note Editor view 에서 note의 validation을 할 때 사용할 겁니다.
이제 save 버튼의 tap 핸들러를 구현해서 이 validation 이 어떻게 이뤄지는지 확인해 보겠습니다.

NotesApp.views.noteEditorTopToolbar = new Ext.Toolbar({
    title: 'Edit Note',
    items: [
        {
            text: 'Home',
            ui: 'back',
            handler: function () {
                NotesApp.views.viewport.setActiveItem('notesListContainer', { type: 'slide', direction: 'right' });
            }
        },
        { xtype: 'spacer' },
        {
            text: 'Save',
            ui: 'action',
            handler: function () {

                var noteEditor = NotesApp.views.noteEditor;

                var currentNote = noteEditor.getRecord();
                // Update the note with the values in the form fields.
                noteEditor.updateRecord(currentNote);

                var errors = currentNote.validate();
                if (!errors.isValid()) {
                    currentNote.reject();
                    Ext.Msg.alert('Wait!', errors.getByField('title')[0].message, Ext.emptyFn);
                    return;
                }

                var notesList = NotesApp.views.notesList;
                var notesStore = notesList.getStore();

                if (notesStore.findRecord('id', currentNote.data.id) === null) {
                    notesStore.add(currentNote);
                } else {
                    currentNote.setDirty();
                }

                notesStore.sync();
  notesStore.sort([{ property: 'date', direction: 'DESC'}]);

                notesList.refresh();

                NotesApp.views.viewport.setActiveItem('notesListContainer', { type: 'slide', direction: 'right' });

            }
        }
    ]
});

이 handler 함수에서 첫번째로 form panel의 getRecord() 메소드를 사용했습니다. 이 메소드는 form에 Note model이 로드되는 reference를 가질 수 있도록 해 줍니다. 그 다음에는 updateRecord() 메소드를 사용합니다. 이 메소드는 이 model reference로 폼 필드로부터 value들을 가져와서 전달해 줍니다.
validation dms Note data model에서 validate()를 call 함으로서 Errors 객체를 return 하도록 합니다. isValid()는 이 과정에서 에러가 있는지 없는지 알 수 있게 해 줍니다. 지난 시간에 했듯이 Note Editor 에서는 title 만이 not null입니다. 그래서 이 앱에서는 그 부분만 체크할 겁니다. 에러를 확인하기 위해서는 model의 validations config 옵션에서 정의했듯이 errors.getByField('title')[0] 메세지를 통해서 확인할 수 있습니다.

이 validation을 통화 하면 이 note를 cache에 add 해야 합니다. 대신 이 데이터가 이전에 있는지 확인하는 기능이 있어야 합니다.
아래 findRecord()메소드를 통해서 이 작업을 간단하게 해 주시구요.

var notesStore = notesList.getStore();
if (notesStore.findRecord('id', currentNote.data.id) === null) {
    notesStore.add(currentNote);
}


update 시키신 후에는 sync() 메소드를 이용해서 완전히 이 데이터를 저장하시면 됩니다. 오라클에서 commit; 기능하고 비슷합니다.
그 다음에 sort() 메소드를 사용해서 note를 날짜 순서로 정렬합니다. 그러면 notes list에 render 할 준비가 끝나는 겁니다.


notesList.refresh();

NotesApp.views.viewport.setActiveItem('notesListContainer', { type: 'slide', direction: 'right' });

type을 보시면 slide와 right가 있을 겁니다. 이것은 화면 전환 할 때 된쪽에서 오른쪽으로 slide하게 화면이 바뀌도록 합니다.

이제 남은 것은 Home button에 handler를 구현하는 겁니다.
이 홈 버튼을 누르면 단순히 Notes List로 돌아가기만 하면 됩니다.

{
    text: 'Home',
    ui: 'back',
    handler: function () {
        NotesApp.views.viewport.setActiveItem('notesListContainer', { type: 'slide', direction: 'right' });'s next
    }
}

한번 화면으로 테스트 해 보고 싶으신가요? 이제 실행 해 보시면 save하고 새 note를 create하는 것들이 다 가능할 겁니다.

***** What’s next
다음에 우리가 구현 할 것은 기존에 존재하는 notes를 수정하고 지우는 기능입니다.
이 기능은 다음 시간에 구현해 보겠습니다.

오늘까지 한 js 소스는 아래와 같습니다.

var App = new Ext.Application({
    name: 'NotesApp',
    useLoadMask: true,
    launch: function () {

        Ext.regModel('Note', {
            idProperty: 'id',
            fields: [
                { name: 'id', type: 'int' },
                { name: 'date', type: 'date', dateFormat: 'c' },
                { name: 'title', type: 'string' },
                { name: 'narrative', type: 'string' }
            ],
            validations: [
                { type: 'presence', field: 'id' },
                { type: 'presence', field: 'title', message: 'Please enter a title for this note.' }
            ]
        });

        Ext.regStore('NotesStore', {
            model: 'Note',
            sorters: [{
                property: 'date',
                direction: 'DESC'
            }],
            proxy: {
                type: 'localstorage',
                id: 'notes-app-store'
            }
        });

        NotesApp.views.noteEditorTopToolbar = new Ext.Toolbar({
            title: 'Edit Note',
            items: [
                {
                    text: 'Home',
                    ui: 'back',
                    handler: function () {
                        NotesApp.views.viewport.setActiveItem('notesListContainer', { type: 'slide', direction: 'right' });
                    }
                },
                { xtype: 'spacer' },
                {
                    text: 'Save',
                    ui: 'action',
                    handler: function () {

                        var noteEditor = NotesApp.views.noteEditor;

                        var currentNote = noteEditor.getRecord();
                        // Update the note with the values in the form fields.
                        noteEditor.updateRecord(currentNote);

                        var errors = currentNote.validate();
                        if (!errors.isValid()) {
                            currentNote.reject();
                            Ext.Msg.alert('Wait!', errors.getByField('title')[0].message, Ext.emptyFn);
                            return;
                        }

                        var notesList = NotesApp.views.notesList;
                        var notesStore = notesList.getStore();

                        if (notesStore.findRecord('id', currentNote.data.id) === null) {
                            notesStore.add(currentNote);
                        } else {
                           currentNote.setDirty();
                        }

                        notesStore.sync();
                        notesStore.sort([{ property: 'date', direction: 'DESC'}]);

                        notesList.refresh();

                        NotesApp.views.viewport.setActiveItem('notesListContainer', { type: 'slide', direction: 'right' });

                    }
                }
            ]
        });

        NotesApp.views.noteEditorBottomToolbar = new Ext.Toolbar({
            dock: 'bottom',
            items: [
                { xtype: 'spacer' },
                {
                    iconCls: 'trash',
                    iconMask: true,
                    handler: function () {
                        // TODO: Delete current note.
                    }
                }
            ]
        });

        NotesApp.views.noteEditor = new Ext.form.FormPanel({
            id: 'noteEditor',
            items: [
                {
                    xtype: 'textfield',
                    name: 'title',
                    label: 'Title',
                    required: true
                },
                {
                    xtype: 'textareafield',
                    name: 'narrative',
                    label: 'Narrative'
                }
            ],
            dockedItems: [
                    NotesApp.views.noteEditorTopToolbar,
                    NotesApp.views.noteEditorBottomToolbar
                ]
        });

        NotesApp.views.notesList = new Ext.List({
            id: 'notesList',
            store: 'NotesStore',
            itemTpl: '
<div class="list-item-title">{title}</div>
' +
                '
<div class="list-item-narrative">{narrative}</div>
',
            onItemDisclosure: function (record) {
                // TODO: Render the selected note in the note editor.
            },
            listeners: {
                'render': function (thisComponent) {
                    thisComponent.getStore().load();
                }
            }
        });

        NotesApp.views.notesListToolbar = new Ext.Toolbar({
            id: 'notesListToolbar',
            title: 'My Notes',
            layout: 'hbox',
            items: [
                { xtype: 'spacer' },
                {
                    id: 'newNoteButton',
                    text: 'New',
                    ui: 'action',
                    handler: function () {

                        var now = new Date();
                        var noteId = now.getTime();
                        var note = Ext.ModelMgr.create(
                            { id: noteId, date: now, title: '', narrative: '' },
                            'Note'
                        );

                        NotesApp.views.noteEditor.load(note);
                        NotesApp.views.viewport.setActiveItem('noteEditor', { type: 'slide', direction: 'left' });
                    }
                }
            ]
        });

        NotesApp.views.notesListContainer = new Ext.Panel({
            id: 'notesListContainer',
            layout: 'fit',
            html: 'This is the notes list container',
            dockedItems: [NotesApp.views.notesListToolbar],
            items: [NotesApp.views.notesList]
        });

        NotesApp.views.viewport = new Ext.Panel({
            fullscreen: true,
            layout: 'card',
            cardAnimation: 'slide',
            items: [
                NotesApp.views.notesListContainer,
                NotesApp.views.noteEditor
            ]
        });
    }
});

이제 저도 Sencha Touch 에 대해 대충 감이 잡히네요.
다음시간에 첫번째 센차터치 앱 Notes 를 완료하겠습니다.
그리고 계속 하이브리드 모바일 앱을 위해 Corona SDK, HTML5, Sencha Touch, Phone Gap 등 관련 기술을 익히고 싶네요.

꾹~~ 꾹~~ 응원 부탁드려요... ~~~~~~~
반응형


반응형

Writing a Sencha Touch Application, Part 2

지난 시간에는 Sencha로 만드는 첫번째 앱인 My Notes 앱을 배웠습니다.
Notes List view를 말들고 있었습니다.
지난시간에 아래 화면까지 작업했습니다.



여기에 아래 mock-up 이미지처럼 툴바에 버튼을 달고 유저가 생성한 Notes들을 랜더링해서 보여주는 기능을 Ext.list를 이용해서 구현할 겁니다.



***** Creating a Data Model in Sencha Touch

우선 데이타 모델을 만들겠습니다. DB를 이용한다면 필드들을 만드는 겁니다.
리스트에 표시할 내용들에 대한 데이터 모델인데요 id,date,title,narrative 등이 있어야 합니다.
이 데이터 모델은 Ext.regModel() 메소드를 이용해서 만들겁니다.

Ext.regModel('Note', {
    idProperty: 'id',
    fields: [
        { name: 'id', type: 'int' },
        { name: 'date', type: 'date', dateFormat: 'c' },
        { name: 'title', type: 'string' },
        { name: 'narrative', type: 'string' }
    ],
    validations: [
        { type: 'presence', field: 'id' },
        { type: 'presence', field: 'title' }
    ]
});

데이타 모델과 관련한 API는 여기를 클릭해서 보시면 도움이 될 겁니다.
Sencha Touch의 이 데이터 모델관련 메소드에서는 내부적으로 validation을 지원합니다.
위에는 id와 title에 validations를 주었습니다. 데이터가 꼭 있어야 된다는 내용입니다. sql로 치면 not null 입니다.
이 validation이 어떻게 동작하는지에 대해서는 나중에 살펴보도록 하죠.
또한 associations 기능도 지원을 하는데요 우리가 만드는 앱에서는 사용하지는 않습니다.
아주 유용한 기능이니까 API를 꼭 참조하세요.

***** Configuring a Sencha Touch Data Store to use HTML5 local storage

이제 데이터를 Cacheg하는 기능이 필요합니다. Ext.regstore() 함수가 데이터 store를 생성하도록 할 겁니다.
아래 소스코드를 참조하세요.

Ext.regStore('NotesStore', {
    model: 'Note',
    sorters: [{
        property: 'date',
        direction: 'DESC'
    }],
    proxy: {
        type: 'localstorage',
        id: 'notes-app-localstore'
    }
});

Ext.regStore() 는 프레임 워크의 Store Manager 와 같이 store를 생성하고 register 합니다. 프레임워크의 다른 클래스들 처럼 여러분은 앱의 데이터 store를 lookup 하고 modify 하는데 이 Store Manager를 사용할 수 있습니다.
우리의 NotesStore의 모델 config 옵션은 Note model 입니다. 우리는 또한 sorters 옵션도 사용해서 날짜 역순으로 표시할 겁니다.
우리의 앱은 브라우저 세션을 이용해서 notes를 저장할 수 있어야 합니다. 그러기 위해서는 store의 proxy로 모델데이터를 로드하고 저장하는 일을 해야 합니다. 그래서 proxy config 옵션을 사용합니다. 이 옵션을 사용함으로서 proxy 는 Ext.data.LocalStorageProxy 클래스의 인스턴스가 됩니다. 이 클래스는 HTML5의 LocalStorage API를 사용해 사용자 브라우저에 모델 데이터를 저장하게 됩니다.

***** Creating a Sencha Touch list

데이터 모델과 store 가 준비 됐으면 이제 notes list 를 생성할 수 있습니다.

NotesApp.views.notesList = new Ext.List({
    id: 'notesList',
    store: 'NotesStore',
    itemTpl: '
<div class="list-item-title">{title}</div>
' +
        '
<div class="list-item-narrative">{narrative}</div>
'
});

복잡한 거는 없습니다. store config 옵션에 이름을 넣습니다. render 하기 위해 itemTpi config 옵션을 사용합니다. 그 안에 있는 태그(markup)은 list-item-title과 list-item-narrative 클래스가 정의 돼 있습니다. 이 클래스는 app.css 파일에서 사용할 겁니다.

.list-item-title
{
    float:left;
    width:100%;
    font-size:90%;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.list-item-narrative
{
    float:left;
    width:100%;
    color:#666666;
    font-size:80%;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.x-item-selected .list-item-title
{
    color:#ffffff;
}
.x-item-selected .list-item-narrative
{
    color:#ffffff;
}

이제 panel의 items config 옵션을 사용해서 container panel에 list를 추가해 보겠습니다.

NotesApp.views.notesListContainer = new Ext.Panel({
    id: 'notesListContainer',
    layout: 'fit',
    html: 'This is the notes list container',
    dockedItems: [NotesApp.views.notesListToolbar],
    items: [NotesApp.views.notesList]
});

시뮬레이터에서 확인하기 전에 dummy note를 집어 넣겠습니다. 그래야 브라우저나 디바이스로 테스트하면 이 dummy 내용이 나올 테니까요. 아래와 같이 data config 옵션을 넣어 보세요.

Ext.regStore('NotesStore', {
    model: 'Note',
    sorters: [{
        property: 'date',
        direction: 'DESC'
    }],
    proxy: {
        type: 'localstorage',
        id: 'notes-app-store'
    },
    // TODO: remove this data after testing.
    data: [
        { id: 1, date: new Date(), title: 'Test Note', narrative: 'This is simply a test note' }
    ]
});

에뮬레이터에서 Notes List 는 아래와 같이 보일 겁니다.



이제 우리의 Notes List view는 거의 완성 됐습니다. 툴바를 만들었고 Note List도 만들었습니다. 그리고 data model,store도 구현했구요 이러한 것들은 이 notes를 cache 하게 될 겁니다. 아직 안된게 두개가 있습니다. 다른 페이지로 넘어가도록 하는 버튼들인데요. 툴바에 있는 New 버튼하고 각각의 List item에 있는 disclosure 버튼입니다.



***** Adding buttons to a Sencha Touch toolbar

아래 소스코드는 New 버튼을 툴바에 add 하는 방법을 보여줍니다.
NotesApp.views.notesListToolbar = new Ext.Toolbar({
    id: 'notesListToolbar',
    title: 'My Notes',
    layout: 'hbox',
    items: [
        { xtype: 'spacer' },
        {
            id: 'newNoteButton',
            text: 'New',
            ui: 'action',
            handler: function () {
                // TODO: Create a blank note and make the note editor visible.
            }
        }
    ]
});

보시면 툴바에 New button을 달 수 있도록 hbox 레이아웃과 spacer를 넣었습니다. 그리고 handler function 도 넣었습니다. 여기에는 나중에 Note Editor view 와 관련한 기능이 추가 될 겁니다.

***** Implementing disclosure buttons in a Sencha Touch list

각 리스트에 달릴 disclosure 버튼을 다는것도 쉽습니다. 아래 코드에서 보듯이 onItemDisclosure function을 사용하시면 됩니다.

NotesApp.views.notesList = new Ext.List({
    id: 'notesList',
    store: 'NotesStore',
    itemTpl: '
<div class="list-item-title">{title}</div>
' +
        '
<div class="list-item-narrative">{narrative}</div>
',
    onItemDisclosure: function (record) {
        // TODO: Render the selected note in the note editor.
    }
});

리스트 아이템에 tap event에 대한 listener를 달기 위해 onItemDisclsure를 override 하고 있습니다. 이 리스너가 하는 일은 유저가 리스트를 탭하면 이를 감지해서 Note Editor view에 알려주는 역할을 하게 될 겁니다.
이 기능은 Note Editor를 만들고 나서 나중에 구현하겠습니다.

***** Where are we?

여기까지 무사히 마쳤다면 아래와 같은 화면을 부실 수 있을 겁니다.



이제 어플리케이션의 main view를 완성했습니다. 두개의 이벤트 핸들러를 구현하는 부분은 남았지만 비쥬얼한 부분은 다 완성됐습니다.

이제 다음시간에는 Note Editor를 만들어 보겠습니다.



아래는 지금까지 한 소스 입니다.

var App = new Ext.Application({
    name: 'NotesApp',
    useLoadMask: true,
    launch: function () {

        Ext.regModel('Note', {
            idProperty: 'id',
            fields: [
                { name: 'id', type: 'int' },
                { name: 'date', type: 'date', dateFormat: 'c' },
                { name: 'title', type: 'string' },
                { name: 'narrative', type: 'string' }
            ],
            validations: [
                { type: 'presence', field: 'id' },
                { type: 'presence', field: 'title' }
            ]
        });

        Ext.regStore('NotesStore', {
            model: 'Note',
            sorters: [{
                property: 'date',
                direction: 'DESC'
            }],
            proxy: {
                type: 'localstorage',
                id: 'notes-app-localstore'
            },
            // TODO: remove this data after testing.
            data: [
                { id: 1, date: new Date(), title: 'Test Note', narrative: 'This is simply a test note' }
            ]
        });

        NotesApp.views.notesList = new Ext.List({
            id: 'notesList',
            store: 'NotesStore',
            itemTpl: '
<div class="list-item-title">{title}</div>
' +
                '
<div class="list-item-narrative">{narrative}</div>
',
            onItemDisclosure: function (record) {
                // TODO: Render the selected note in the note editor.
            }
        });

        NotesApp.views.notesListToolbar = new Ext.Toolbar({
            id: 'notesListToolbar',
            title: 'My Notes',
            layout: 'hbox',
            items: [
                { xtype: 'spacer' },
                {
                    id: 'newNoteButton',
                    text: 'New',
                    ui: 'action',
                    handler: function () {
                        // TODO: Create a blank note and make the note editor visible.
                    }
                }
            ]
        });

        NotesApp.views.notesListContainer = new Ext.Panel({
            id: 'notesListContainer',
            layout: 'fit',
            html: 'This is the notes list container',
            dockedItems: [NotesApp.views.notesListToolbar],
            items: [NotesApp.views.notesList]
        });

        NotesApp.views.viewport = new Ext.Panel({
            fullscreen: true,
            layout: 'card',
            cardAnimation: 'slide',
            items: [NotesApp.views.notesListContainer]
        });
    }
});

앞으로 두 번만 더 다루면 센차터치로 만드는 첫 번째 앱이 완성됩니다.
정말 기대가 되는데요.
다음 시간에 또 뵙구요... 꾹 꾹 추천 한방 눌러 주세요.... ~~~~
반응형


반응형
오늘은 Corona로 그럴듯하게 눈발 흩날리는 느낌을 한번 줘 보겠습니다.

ramdom하고 translate, newImageRect 를 이용해서 이 효과를 줘 봤습니다.

우선 먼저 소스를 보고 분석 해 보죠.

display.setStatusBar( display.HiddenStatusBar )
local mRand = math.random; math.randomseed(os.time())
local xGravity,yGravity,xWind, yWind = 0,18,45,2
local bg = display.newImage("usa_bg2.png")
local snow = display.newGroup()
local function animateSnow( event )
    for i=1, snow.numChildren do
        local flake = snow[i]
        flake:translate((flake.xVelocity+xWind)*0.1,(flake.yVelocity+yWind)*0.1)
        if flake.y > display.contentHeight then
            flake.x, flake.y = mRand(display.contentWidth), mRand(60)
        end
    end
    if mRand(64) == 1 then xWind = 0-xWind; end
end

local function initSnow(snowCount)
    for i=1,snowCount do
        --local flake = display.newImageRect(snow,"flake.png",1,1)
        local flake = display.newImageRect(snow,"drophose.png",6,6)
        if mRand(1,2) == 1 then flake.alpha = mRand(25,100) * .01; end
        flake.x, flake.y = mRand(display.contentWidth), mRand(display.contentHeight)
        flake.yVelocity, flake.xVelocity = mRand(60), mRand(50)
    end
    Runtime:addEventListener("enterFrame", animateSnow)
end
initSnow(1000)

처음엔 아이폰의 status바 없애는 부분이고요.
두번째 줄은 mRand라는 변수에 랜덤값을 넣구요 randomseed로는 os time을 사용합니다.
중력은 y방향으로반 18을 주고 바람의 첫번째 값은 x로 45 y로 2 입니다.
다음는 배경화면을 깐겁니다.
5번째 줄에 그룹을 만들었는데요 이 그룹은 눈을 담을 겁니다.
눈 이미지는 하나만 사용할 거구요. 이 이미지를 1000개 만들어서 이 그룹에 넣게 됩니다.

다름에 animateSnow 함수가 나옵니다.

이 부분이 눈발을 흩날리는 부분입니다.
처름에 for문을 돌리는데요. snow그룹안에 있는 인자들의 갯수만큼 for문을 돌립니다.
 맨 아랫줄 보시면 1000이라는 숫자가 보이죠? snow그룹안에는 인자가 천개가 될 테니까 1000번 돌겁니다.
그 다음 flake라는 변수에 snow의 i번째 인자를 담구요 이 flake를 translate 시킵니다.
이 falke가 y방향으로 밖으로 나가게 되면 다시 화면 안으로 들어오게만들구요.

여기서 falke의 방향하고 속도는 initSnow에서 랜덤하게 정해진 값입니다.

initSnow함수는 snowCount만큼 for문을 돌립니다. 역시 1000개가 되겠죠?
flake에 imageRect를 할당합니다.
여기서 Rect에 크기를 조절하면 싸리눈도 되고 함박눈도 됩니다.
그리고 이 flake에 alpha값을 랜덤하게 지정해 줍니다.
위치도 랜덤하게 지정하구요.
다음으로 속도도 랜덤하게 적용합니다.

이렇게 1000개의 flake를 만들구요. 그 다음에 animateSnow를 Runtime의 enterframe으로 실행 시킵니다.

맨 마지막 줄은 이 initSnow함수를 실행시키는데 인자 값으로 1000을 전달합니다.
이 숫자가 적으면 눈이 적게 내리겠고 많으면 눈이 많이 내래겠죠?



완성된 화면입니다.
눈이 바람에 흩날리면서 내립니다.
눈(flake)의 갯수나 imageRect 사이즈를 조정하시면 눈을 많이 내리게도 할 수 있고 조금 내리게도 할 수 있고 싸리눈이 될 수도 있고 함박눈이 될 수도 있습니다.

이렇게 그럴듯한 눈내리는 효과를 단 25줄로 처리해 버립니다.
코로나SDK 아주 훌륭합니다...
이 효과 잘 활용하시구요. 꾹~~ 꾹~~ 추천 부탁드립니다.~~~~~
반응형


반응형
안녕하세요.
드디어 Sencha Touch로 앱을 만들기로 했습니다.
이번주 들어서 갑자기 HTML5로 게임 만들기하고 Sencha Touch 앱하고 본격적으로 공부하고 싶어지더라구요.

오늘은 첫번째 모바일 웹으로 간단한 Note 기능을 만들겠습니다.

이 앱의 기능은 유저가 Notes를 받아서 이것을 디바이스에 저장하고 앱을 시작하면 그 Notes들을 볼 수 있도록 하겠습니다.

이 앱을 만들기 위해서는 아래 작업들을 해야 합니다.
- Scencha Touch Application의 block들을 만든다.
- 리스트 뷰를 이용해 정보를 렌더링 한다.
- form elements들을 이용해서 정보를 editing 한다.
- 브라우저 세션을 이용해 클라이언트 사이드 data persistence를 구현한다.
- multi-view 어플리케이션으로 여러 화면을 navigation할 수 있도록 한다.


완성된 화면인데요.
New를 누르면 간단한 Note를 할 수 있는 화면이 나오고 위 화면은 앱을 켰을 때 그 Note들이 출력되도록 하는 List 화면입니다.

***** 어플리케이션 개요
- note를 생성할 수 도록 한다.
- 현재의 note를 수정할 수 있도록 한다.
- note를 delete 할 수 있도록 한다.
- 브라우저 세션을 이용해 어플리케이션이 동작하면서 device에 note를 저장할 수 있도록 한다.

이러한 기능들을 염두에 두고 비쥬얼한 디자인을 한번 보겠습니다.

***** 메인 뷰 디자인하기

우리가 첫번째로 만들어야 할 부분은 note를 수정할 수 있는 화면입니다.
이 화면은 form 을 이용해 만들겁니다. 앞으로 이 화면은 Note Editor라고 부르도록 하겠습니다.
대략 아래와 같이 될 겁니다.


이 Note Editor를 Sencha Touch components를 이용해서 어떻게 구현할까요?
아래 그림을 보세요.



또한 우리는 기존에 있던 note들을 리스트로 랜더링해서 보여주는 화면이 필요합니다.
이 화면이 아마 이 애플리케이션의 메인 화면이 될 겁니다.
또한 이 화면은 Note Editor와 연결 돼 있어야 합니다. 아래 대략적인 그림이 있습니다.



그리고 아래는 우리가 사용할 Sencha Touch components들 입니다.



이 두 화면 이외에 보이지는 않지만 이 앱에 꼭 필요한 component가 더 있습니다.
이 컴포넌트는 이 앱의 viewport와 같은 기능을 할거고 랜더링이나 Notes List와 Note Editor를 전환하는 부분을 담당하게 될 겁니다.
이 기능을 구현하기 위해 Notes List와 Note Editor가 layout's cards가 되는 Panel을 구성할 겁니다.



***** Sencha Touch application 의 block들 만들기

우리는 index.html, app.css, app.js 이렇게 세개의 파일을 만들겁니다.
index.html은 앱을 launch 하기 위한 파일입니다. 이 파일에는 기본 Sencha Touch framework와 우리가 사용할 app.js, app.js 파일들에 대한 정보가 들어 있습니다.

<!DOCTYPE html>
<html>
<head>
    <title>Getting Started</title>

<script src="sencha-touch-debug.js" type="text/javascript"></script>
<link href="sencha-touch.css" rel="stylesheet" type="text/css" />
<link href="app.css" rel="stylesheet" type="text/css" />
<script src="app.js" type="text/javascript"></script>
</head>
<body></body>
</html>



app.js와 app.css는 이 앱에서 사용할 자바스크립트와 css 파일입니다.

app.js에서 우리가 할 최초의 작업은 어플리케이션을 instantiate 하는 작업입니다.
var App = new Ext.Application({
    name : 'NotesApp',
    useLoadMask : true,
    launch : function () {
    }
})


Ext.Application 클래스는 Sencha Touch application 임을 말합니다.
자세한 사항은 http://docs.sencha.com/touch/1-1/#!/api/Ext.Application 에 있습니다.
새 application을 instantiate 하게 되면 자동적으로 전역번수 NotesApp이 생성되고 아래의 namespaces들이 만들어 집니다.
- NotesApp
- NotesApp.views
- NotesApp.models
- NotesApp.controllers
- NotesApp.stores

그리고 launch() 함수는 오직 한번만 실행됩니다. 여기에는 우리가 생성할 application의 view가 들어가게 됩니다.
launch: function () {

    NotesApp.views.viewport = new Ext.Panel({
        fullscreen: true,
        html:'This is the viewport'
    });
}


우리 앱의 viewport는 Ext.Panel 입니다. 이 panel에는 Notes List와 Note Editor가 그려지게 될 겁니다.
여기서 fullscreen config 옵션을 true로 하게 되면 이 panel이 브라우저의 모든 공간을 활용하게 됩니다.
여기에는 monitorOrientation config도 true로 할 수 있습니다. 이렇게 되면 모바일의 경우 orientation이 바뀌게 되면 화면도 바뀌게 됩니다.

여기까지 작업한 내용을 device나 simulator에서 돌리시면 아래와 같은 화면을 보실 수 있을겁니다.



***** Creating the Notes List container
Notes List는 앱이 실행될 때 유저에게 보여질 화면입니다. 아래 그림에서 보듯이 여기에는 툴바와 리스트가 있게 될 겁니다.
한번 구현해 보겠습니다.

첫번째로 우리는 툴바와 리스트를 display 하게 될 panel을 만들어야 합니다.



우리는 이 panel을 notesListContainer라고 부르겠습니다.
NotesApp.views.notesListContainer = new Ext.Panel({
    id : 'notesListContainer',
    layout : 'fit',
    html: 'This is the notes list container'
});


여기에 툴바와 리스트를 추가하기 전에 이 viewport안에서 rendering 작업을 해야 합니다.
var App = new Ext.Application({
    name: 'NotesApp',
    useLoadMask: true,
    launch: function () {

        NotesApp.views.notesListContainer = new Ext.Panel({
            id : 'notesListContainer',
            layout : 'fit',
            html: 'This is the notes list container'
        });

        NotesApp.views.viewport = new Ext.Panel({
            fullscreen : true,
            layout : 'card',
            cardAnimation : 'slide',
            items: [NotesApp.views.notesListContainer]
        });
    }
})


여기에 왜 card 레이아웃을 사용했을 까요? 왜냐하면 Notes List container와 Note Editor 들은 cards 들이기 때문입니다.
대부분의 모바일 앱이 그렇듯이 이 cards 들은 slide 애니메이션을 사용해서 display 될 겁니다.

이 소스를 device나 simulator에서 체크해 보시면 아래와 같은 화면을 보실 수 있을 겁니다.



뭐가 잘 못 된 걸까요? 툴바와 notes list가 안 보이네요. 이것들은 아래에서 마저 작업 하겠습니다.

***** Adding a Sencha Touch toolbar to a panel

아래와 같이 툴바를 정의할 수 있습니다. 여기에 버튼이 들어가야 되는데요 이건 나중에 작업하겠습니다.
NotesApp.views.notesListToolbar = new Ext.Toolbar({
    id: 'notesListToolbar',
    title: 'My Notes'
});


디바이스나 시뮬레이터로 아래처럼 화면이 출력되는지 확인해 보세요.



***** 다음 작업
오늘은 여기서 마치도록 하고요 다음 글에서는 방금 만든 notes list를 view에 추가하겠습니다. 그리고 note를 만들고 수정하는 기능들도 작업하겠습니다.

지금까지의 소스는 아래와 같습니다.

var App = new Ext.Application({
    name: 'NotesApp',
    useLoadMask: true,
    launch: function () {

        NotesApp.views.notesListToolbar = new Ext.Toolbar({
            id: 'notesListToolbar',
            title: 'My Notes'
        });

        NotesApp.views.notesListContainer = new Ext.Panel({
            id: 'notesListContainer',
            layout: 'fit',
            html: 'This is the notes list container',
            dockedItems: [NotesApp.views.notesListToolbar]
        });

        NotesApp.views.viewport = new Ext.Panel({
            fullscreen: true,
            layout: 'card',
            cardAnimation: 'slide',
            items: [NotesApp.views.notesListContainer]
        });
    }
})

지금까지의 소스는 아래 파일을 다운받아서 보시면 됩니다.



추천 추천 부탁드려요. ~~~~~~~ ~~~~~~~
반응형


반응형
어제 SGF(Simple Game Framework)를 이용해서 HTML5 로 PingPong Game 을 만들기 시작했습니다.

index.html 과 styles.css 파일을 만들고 js폴더 밑에 SGF 를 넣었습니다.

index.html 에서는 data-screen="screen", data-game="Pong" 이 두가지 태그를 유심히 보실 필요가 있습니다.
data-screen 으로는 div id 가 screen인 것을 사용한다는 의미입니다.
이 부분이 없으면 해당 html 의 body 부분을 screen으로 사용할 겁니다.
그리고 data-game 을 Pong으로 한 것은 Pong이라는 폴더 밑에 있는 game관련 소스를 참고할 것이라는 의미입니다.
앞으로 만들 PingPong 게임의 objects 와 behavior 관련 소스코드들은 바로 이 폴더 밑에 위치할 겁니다.

일단 SGF로 게임을 만드려면 main.js 를 만들어야 합니다.
아래 코드를 Pong 폴더 안에 main.js 파일로 저장해 주세요.

// Import required classes
var Game = SGF.require("Game");
var Input = SGF.require("Input");
var Rectangle = SGF.require("Rectangle");
var Label = SGF.require("Label");
// Get a reference to our Game instance
var myGame = Game.getInstance();
// Get a reference to our game's Input instance
var myInput = myGame.input;
var game_height = 400;
var game_width = 400;
myGame.getScript("Paddle.js", function(){
// left paddle
myGame.leftPaddle = new Paddle();
myGame.leftPaddle.setPosition(0, 150);
myGame.addComponent(myGame.leftPaddle);
});

main.js에서는 처음에 Game 을 require 합니다.
그리고 Input,Rectangle,Lable 변수에 SGF의 해당 메소드들을 대입했습니다.
다음 myGame 변수에는 Game의 getInstance() 메소드를 대입했습니다.


SGF의 API를 보면 위와 같이 getInstance 에 대한 설명을 보실 수 있습니다.
항상 main.js의 첫번째 파트에 이 getInstance메소드가 오게 된다고 나와 있네요.

그리고 이 인스턴스에 input을 사용하기 위해 myInput 이라는 변수에 담습니다.
다음줄엔 game_height와 game_width 변수에 400을 대입합니다.

그리고 myGame 인스턴스에 getScript 메소드를 이용해서 Paddle.js를 사용해서 Paddle을 화면에 그려 넣습니다.
여기선 일단 leftPaddle 을 만들고 그 위치를 정하고 컴포넌트에 add를 했습니다.

아직 Paddle.js를 만들지는 않았습니다. main.js 까지 완성하면 아래 화면을 보실 수 있습니다.

어제 만들었던 하얀 바탕이 까맣게 변했죠?
Paddle을 만들었지만 아직 Paddle.js를 완성하지 않았기 때문에 화면에는 나타나지 않습니다.

아래 소스는 paddle.js 입니다.
// Paddle.js
var Paddle = Class.create(Rectangle, {
initialize: function($super){
$super();
this.height = 100;
this.width = 20;
this.color = "0011FF";
this.isPlayerOne = true;
},
setPosition: function(x, y){
this.x = x;
this.y = y;
},
getPosition: function(){
return {
'x': this.x,
'y': this.y
}
},
setIsPlayerOne: function(bool){
this.isPlayerOne = bool;
},
checkInput: function(){
if (this.isPlayerOne == false) {
if (myInput.isKeyDown(Input.KEY_UP)) {
if (this.y > 0) {
this.y = this.y - 10;
}
}
else
if (myInput.isKeyDown(Input.KEY_DOWN)) {
// x,y are taken from the left corner
if (this.y < game_height - this.height)
this.y = this.y + 10;
}
}
else {
if (myInput.isKeyDown(65)) { // 'A'
if (this.y > 0) {
this.y = this.y - 10;
}
}
else
if (myInput.isKeyDown(90)) { // 'Z'
// x,y are taken from the left corner
if (this.y < game_height - this.height)
this.y = this.y + 10;
}
}
},
update: function(){
this.checkInput();
}
});

처음 Paddle을 생성합니다. Rectangle 을 이용해서 생성했는데요. height는 100 width 는 20이고 color 는 0011FF 로 파란색 입ㅂ니다.
그리고 이 Paddle의 isPlayerOne 은 true 입니다.

setPosition,getPosition,setIsPlayerOne  함수들은 나중에 보겠습니다.

우선 checkInput 함수를 보겠습니다.

맨 처음 isPlayerOne을 체크합니다. 이 탁구게임은 2인용이기 때문에 왼쪽 Player 인지 오른쪽 Player 인지를 알아야 합니다.
우선 leftPaddle 에 inPlayerOne 을 true로 했다는 것을 기억하세요.

맨 처음 if 문은 isPlayerOne 이 false 인 경우에 실행하는 것이니까 오른쪽 paddle에 해당 되는 겁니다. 아직 이 오른쪽 패들은 만들지 않았으니까요 저 아래 else를 보겠습니다.
else 부분 (왼쪽 paddle)에서는 key 번호 65번이 눌려졌는지를 체크합니다.
이 65번은 키보드의 A 버튼을 말합니다.
이 키가 눌려졌으면 y 좌표가 10씩 줄어듭니다.
그리고 90(Z) 가 눌려졌으면 y 좌표가 10씩 늘어납니다.

이제 index.html을 refresh 하시면 위와 같은 화면을 보실 수 있습니다.
a,z 키를 눌러 보세요. 이 패들이 아래 위로 움직일 겁니다.

그럼 이제 right paddle 도 넣어 볼까요?

아래 코드를 left paddle 아래에 추가하세요.
 myGame.rightPaddle = new Paddle();
  myGame.rightPaddle.setPosition(game_width - myGame.rightPaddle.width, 150);
  myGame.rightPaddle.setIsPlayerOne(false);
  myGame.addComponent(myGame.rightPaddle);

leftPaddle이랑 다른 부분은 setIsPlayerOne을 false로 한 것입니다.
left가 아니라 right란 것을 알리기 위해서죠.


오른쪽 paddle은 키보드의 위 아래 화살표를 누르면 위 아래로 움직이게 됩니다.
이 내용은 Paddle.js 에서 보셨죠?

이제 두개의 패들을 다 만들었고 유저가 이 객체들을 움직일 수 있도록 했습니다.

지금까지 한 소스는 아래 압축파일에 있습니다.


다음시간에는 이어서 공을 만들고 Score를 만들어 보겠습니다.

꾹~~~ 추천 추천 부탁 드려요 ~~~~~ ~~~~~~~~
반응형


반응형
드디어 HTML5로 게임 만들기를 시작하려고 합니다.
그동안 사전 정지작업의 일환으로 HTML5,CSS3,Javascript 와 관련해서 살펴봤습니다.
HTML5로 게임만들기를 거쳐서 HTML5 모바일 게임을 만드는게 제 개인적인 목표입니다.

개인적으로 2년여 전 부터 모바일 앱 프로그래밍을 시작했고 작년 중순부터 Corona SDK로 크로스 플랫폼 모바일 애플리케이션을 개발하고 있습니다.

Corona SDK 이외에 HTML5 를 통한 앱 개발도 장차 크로스플랫폼 모바일 프로그래밍으로 아주 전망이 있을 것 같아 이 공부를 시작했습니다.

HTML5로 게임만들기 이외에 센차 터치나 phone Gap (폰갭)을 통한 웹 앱 개발도 제 주요 관심 분야 중 하나 입니다.
조만간 이 공부도 본격적으로 하게 되면 posting 하도록 하겠습니다.

이제 HTML5로 게임만들기를 시작해 보죠.

처음 시작할 게임은 탁구(Ping Pong)게임 입니다.

완성된 화면은 아래와 같을겁니다.


사각형 게임 화면에 빨간 정사각형(공) 이 있고 두개의 파란 Paddle 이 있습니다.
그리고 상단에는 게임 제목과 점수가 표시 됩니다.
공은 벽에 튀면서 움직이고 이 두개의 paddle로 공을 막아내는 게임입니다.

이 게임은 프레임워크로 SGF(Simple Game Framework)를 사용할 겁니다.
이 프레임워크에 대한 설명은 http://sgfjs.org/ 에 가시면 보실 수 있습니다.

이 프레임워크의 소스와 API 는 https://github.com/TooTallNate/Simple-Game-Framework 에 가시면 다운 받으실 수 있습니다.
다운받으신 후 압축을 푸시고 doc폴더의 index.html 을 실행하시면 아래와 같은 API화면을 보실 수 있습니다.


이 SGF 에 대해서는 따로 다루지는 않겠습니다. 탁구게임을 진행하면서 그때그때 필요함 부분을 설명하겠습니다.
궁금하신분은 이 API를 참조하세요.

우선 오늘은 html 파일과 css 파일을 만들어 보겠습니다.

아래와 같이 index.html을 만들어 보세요.
<!DOCTYPE HTML>
<html>
    <head>
        <title>Pong</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <!-- For IE. Force the browser to the most current rendering mode. -->
        <meta http-equiv="X-UA-Compatible" content="IE=edge" >

        <!-- A few basic styles. These are NOT mandatory for SGF... -->
        <link href="styles.css" type="text/css" rel="stylesheet" />
       
        <script type="text/javascript" src="js/SGF.js"
            data-debug="true"
            data-prototype="lib/prototype.js"
            data-swfobject="lib/swfobject.js"
            data-screen="screen"
            data-game="Pong"></script>
    </head>
    <body>
      <div id="screen"></div>
    </body>
</html>

위 소스를 보시면 처음 <!DOCTYPE HTML> 는 HTML5를 사용하겠다는 의미입니다.
브라우저에게 HTML5를 사용하겠다고 알려주는 것이죠.
그리고 밑으로 내려와서 <link href 태그는 외부 css를 사용하겠다는 겁니다.
그리고 script 태그를 보면 js/SGF.js 를 불러왔습니다.

이 JavaScript는 SGF 프레임워크를 사용할 수 있도록 서비스를 제공해 줍니다.
이 게임에서 사용될 SGF 소스(js 폴더 밑에 있는 있어야 함)는 아래 파일을 다운 받으세요.


이 압축파일을 index.html이 있는 폴더에 압축을 푸시면 됩니다.

이제 css 파일을 만들어 보겠습니다.
이 css 파일 이름은 styles.css가 되야 하겠죠? index.html 에서 그 파일에 링크를 걸었으니까요.

body {
    margin:0 0 0 0;
    width:100%;
    height:100%;
    text-align:center;
}

#screen {
    width:400px;
    height:400px;
    border:Solid 1px #000000;
    margin:0 auto;
}

내용은 별다른게 없습니다. screen 크기가 400X400 픽셀 크기로 지정한게 답니다.

이 index.html 을 브라우저에서 실행하면 아래와 같은 화면을 보실 수 있습니다.

그냥 검은 테두리가 있는 흰 바탕화면 뿐입니다.
저 검은 테두리는 css에서 border:Solid 1px #000000; 를 선언했기 때문에 나타난 겁니다.

자 오늘은 여기까지 하구요.

내일부터 여기에 paddle, ball, score 같은 objects 들을 배치하고 이 objects 들을 control 해 보겠습니다.

추천 추천... 꾹 ~~ 눌러 주세요. ~~~~~
반응형

WebView, Video and Network Updates

2012. 2. 13. 06:43 | Posted by 솔웅


반응형
아래 내용은 Corona SDK Web site 에 원본이 있습니다.
아래 내용은 이 원본을 번역한 내용입니다.

===========
=========== =========== =========== =========== ===========

WebView, Video and Network Updates



이번에 native Web/Video objects 가 릴리즈 되면서 API에 몇가지 중요한 기능이 추가 됐습니다. 또한 Network library에 몇가지 아주 유용한 기능이 추가 됐구요.

***** Web View Updates

아래와 같이 native web view object 들의 상태를 back이나 forward로 control 할 수 있습니다. back()/forward() 메

소드를 써서 간단하게 조정 가능합니다.

local webview = native.newWebView( 0, 0, 320, 240 )
webview:request( "http://www.anscamobile.com" )
webview:request( "http://www.google.com" )
webview:back()   -- goes back to www.anscamobile.com
webview:forward() -- goes forward to www.google.com

canGoBack/canGoForward

위 두 메소드와 관련해서 당신의 web view object가 back이나 forward가 가능한지에 대해 체크할 수 있습니다.
예를 들어 web view object를 처음 만들었다면 이 web view object는 단지 하나만 있기 때문에 back()을 할 수도 없고

forward()를 할 수도 없습니다. 이럴 경우 canGoBack/canGoForward 모두 false 가 됩니다.

print( webview.canGoBack )  -- false

좀 더 자세한 내용은 native.newWebView() documentation을 참조하세요.



*****Video Properties and Phases


새로운 video objects와 관련해서 isMuted 프로퍼티가 새로 선 보였습니다. 이것은 get/set 프로퍼티 입니다. 여러분의 앱에 video

를 임베딩 하는데 있어 좀 더 유연하게 콘트롤 할 수 있도록 도와 줄 겁니다. 또한 새 phase 프로퍼티도 생겼는데요 video 리스너가 그것입니

다. 이 리스너에는 ready와 ended phases 가 있습니다.

아래 예제를 참고하세요.

local function videoListener( event )
    if event.phase == "ready" then
        print( "Video is ready." )

    elseif event.phase == "ended" then
        print( "Video playback has ended." )
    end
end

local videoObj = native.newVideo( 0, 0, 320, 480 )
videoObj:addEventListener( "video", videoListener )

-- load a video and jump to 0:30
videoObj:load( "myvideo.m4v", system.DocumentsDirectory )
videoObj:play()

좀 더 자세한 내용은 native.newVideo() documentation을 참조하세요.


***** Network Library

이 native object 관련 내용을 추가하면서 networkRequest 가 한가지 status와 url 프로퍼티를 갖게 됐습니다.
이것은 network request들을 콘트롤 하는데 유용하게 이용될 수 있습니다.
현재까지는 Mac/iOS/Android 에만 지원 됩니다. (Windows 에서는 조만간 지원될 예정입니다.). 이 두 새로운 프로퍼티를 사용하는 방법

에 대한 예제가 아래 있습니다.


local function networkListener( event )
    local status = event.status
    local url = event.url

    print( "The url " .. url .. " returned a status code of: " .. status
end

network.request( "http://www.google.com", "GET", networkListener )

좀 더 자세한 사항은 Network API documentation 을 참조하세요.

이 기능들을 이용하시려면 코로나 최신 버전을 사용하셔야 합니다.
최신버전은 subscriber들에게만 오픈 돼 있습니다.

추천 버튼 꾹꾹 눌러 주세요. ~~~~~~~~~~~~~~
반응형

Tutorial: Text Input with Native UI

2012. 2. 10. 00:24 | Posted by 솔웅


반응형
아래 내용은 아래 블로그에서 그 원본을 보실 수 있습니다.
http://blog.anscamobile.com/2012/02/tutorial-text-input-with-native-ui/


Text Input with Native UI

아래 내용은 코로나 SDK로 Text Field와 Text Box를 사용할 때 유용한 방법에 대해 설명한 내용입니다.

***** Text Fields vs. Text Boxes

텍스트 필드와 텍스트 박스는 아래와 같은 각각의 특징이 있습니다.

Text Fields
- 1줄 입력 기능을 제공한다. (스크롤 기능이 없다.)
- password fields로 이용할 수 있다.
- 오직 숫자만 입력하도록 설정할 수 있다. (전화기의 숫자 키보드가 나옴.)

Text Boxes
- 여러 줄 입력 기능을 제공한다. (스크롤 기능이 있다.)
- text만 보이도록 하기 위해 background를 숨길 수 있다.
- read only로 세팅할 수 있다.

아래는 두 가지 모두에 있는 기능들입니다.
- 폰트, 글자 사이즈, 글자 색을 바꿀 수 있다.
- 배경색을 바꿀 수 있다.
- userInput listening 기능이 있다.

더 자세한 신택스를 보려면 아래를 참조하세요.

Native Text Fields Documentation
Native Text Boxes Documentation 
   



***** Events and Listeners   

userInput 이벤트를 어떻게 감지하는지에 대해 알아보겠습니다.
텍스트 필드나 텍스트 박스를 생성할 때 이 userInput 이벤트 리스너를 달 수가 있습니다.
그러면 그 이벤트가 일어나는 것을 감지해서 어떤 특정한 기능을 넣을 수가 있습니다.

이 userInput 이벤트에는 몇가지의 phases가 있습니다.

began: 이 단계는 유저가 스크린에 키보드를 나오게 하는 순간입니다. 이 경우 키보드가 중요한 객체를 가리지 않게 하기 위해 위치를 변경하는 등의 기능을 넣을 수 있습니다.
edited: 이 단계는 유저가 텍스트 필드나 텍스트 박스에 타이핑을 하는 동안 일어납니다.
ended: 텍스트 필드나 텍스트 박스가 focus를 잃는 단계입니다. 예를 들어 다른 텍스트 필드/박스 를 tap할 때 등입니다.
submitted: 유저가 return/enter 키를 눌렀을 때 입니다. 즉 텍스트 필드/박스 의 내용을 submit 할때라고 얘기할 수 있습니다.

아래 userInput 리스너 를 이용하는 예제 코드가 있습니다.

local function onUserInput( event )
    if event.phase == "began" then
        print( "Keyboard has now shown up." )

    elseif event.phase == "edited" then
        print( "User has entered text." )

    elseif event.phase == "ended" then
        print( "We have lost focus." )

    elseif event.phase == "submitted" then
        print( "User is done editing, let's do something with text." )
    end
end

***** Not Normal Display Objects!
확실히 알아둬야 할 것은 native text field나 native text box는 Corona display 객체가 아닙니다.
말 그대로 native입니다. Corona SDK의 object들이 아니라 그 핸드폰의 객체들인거죠. 정확히는 그 핸드폰의 OS의 객체라고 하면 더 정확할 겁니다.
이 native객체들은 다른 Corona SDK의 객체들 처럼 위치나 투명도 같은것을 바꿀수는 있어도 Corona SDK내에서 다른 객체들과 함께 Group화 할 수는 없습니다.
그러면 화면전환등을 할 때 다른 객체들과 같이 움직이지 않을 겁니다. 보기가 조금 어글리 할 겁니다.
이러한 것을 보기 좋게 하는 방법이 있습니다. 다른 객체들처럼 움직이는 것 같이 보이도록 하는 거죠.

아래 몇가지 팁이 있습니다.
- native 객체들을 표현할 때 placeholders처럼 보이도록 text object를 만듭니다. 그리고 그 바탕에 사각형이나 rounded 사각형을 놓구요.
  여기서 text object나 사각형들은 모두 Corona SDK의 객체들입니다.
  사용자가 placeholder를 터치하면 그때 native 객체가 나오도록 합니다. 즉 유저가 editing할 때는 이 native 객체가 나와 있도록 하는 것이죠.
  유저가 입력을 끝내면 다시 native 객체를 없애고 다시 placeholder를 표시합니다. 이 때 물론 유저가 입력한 값이 해당 placeholder에 보이겠죠.
- placeholder objects들에 대해 transitions 효과를 줍니다.
- hasBackground 프로퍼티를 이용해서 텍스트 박스의 백그라운드를 안보이도록 합니다. 그리고 여러분의 백그라운드를 넣습니다.
  이 백그라운드는 일반적인 Corona SDK 객체를 이용합니다. 그래서 그 텍스트 박스를 코로나 내부 객체와 섞이도록 합니다.
 
이러한 방법들은 여러분들이 할 수 있는 여러 방법중에 극히 일부분입니다.
이러한 방법을 기초로 여러분들 나름대로 여러 효과들을 내실 수 있을 겁니다.

마지막으로 한가지 중요한 점은 이 native objects들은 object:removeSelf() 와 nil값을 대입하는 과정을 거치면서 분명히 메모리 관리를 해 주셔야 한다는 점 입니다.

***** Simulator Notes
현재 native text box와  native text field는 Mac Simulator, Xcode Simulator 그리고 디바이스에서 작동을 합니다.
맥 시뮬레이터로 테스트를 할 경우 Xcode 시뮬레이터나 디바이스에서 반드시 테스트를 해 보셔야 합니다.
왜냐하면 맥 시뮬레이터(Mac)에서 보이는 것과 실제 기계(iOS)에서 보이는 것이 다를 수가 있습니다.

맥 시뮬레이터에서는 리스너나 이벤트 처리 같은 것을 테스트 할 수 있어 개발 시간을 save해 주는 잇점이 있지면 실제 display는 다르게 작동되므로 반드시 기계에서 테스트해 보셔야 합니다.
가장 좋은 테스트 도구는 실제 device에 빌드해서 하는 것 입니다.

***** Basic Note App
아래 간단하게 Note를 할 수 있는 Note App을 한번 만들어 보겠습니다.
텍스트 필드나 텍스트 박스를 생성하는 예제 이구요 외부 소스로부터 가져와서 다이나믹하게 내용을 뿌려주는 효과도 보실 수 있을겁니다.
(여기서 외부 소스로는 text 파일이 사용됩니다.)

모든 코드는 하나의 코드파일에  들어갈 겁니다. 즉 main.lua한 파일에 다 들어갈 겁니다.

1. Creating the Interface
첫번째 단계는 아주 단순합니다. 앱의 배경을 만들고 위에 타이틀 바를 만들고 두개의 버튼 위젯을 만듭니다.

display.setStatusBar( display.DefaultStatusBar )

local widget = require "widget"
local sbHeight = display.statusBarHeight
local tbHeight = 44
local top = sbHeight + tbHeight

-- forward declarations
local titleField, noteText, loadSavedNote, saveNote

-- create background for the app
local bg = display.newImageRect( "stripes.jpg", display.contentWidth, display.contentHeight )
bg:setReferencePoint( display.TopLeftReferencePoint )
bg.x, bg.y = 0, 0

-- create a gradient for the top-half of the toolbar
local toolbarGradient = graphics.newGradient( {168, 181, 198, 255 }, {139, 157, 180, 255}, "down" )

-- create toolbar to go at the top of the screen
local titleBar = widget.newTabBar{
top = sbHeight,
gradient = toolbarGradient,
bottomFill = { 117, 139, 168, 255 },
height = 44
}

-- create embossed text to go on toolbar
local titleText = display.newEmbossedText( "NOTE", 0, 0, native.systemFontBold, 20 )
titleText:setReferencePoint( display.CenterReferencePoint )
titleText:setTextColor( 255 )
titleText.x = 160
titleText.y = titleBar.y

-- create a shadow underneath the titlebar (for a nice touch)
local shadow = display.newImage( "shadow.png" )
shadow:setReferencePoint( display.TopLeftReferencePoint )
shadow.x, shadow.y = 0, top
shadow.xScale = 320 / shadow.contentWidth
shadow.yScale = 0.25

-- create load button (top left)
local loadBtn = widget.newButton{
label = "Load",
labelColor = { default={255}, over={255} },
font = native.systemFontBold,
xOffset=2, yOffset=-1,
default = "load-default.png",
over = "load-over.png",
width=60, height=30,
left=10, top=28
}

-- onRelease listener callback for loadBtn
local function onLoadRelease( event )
loadSavedNote()
end
loadBtn.onRelease = onLoadRelease -- set as loadBtn's onRelease listener

-- create save button (top right)
local saveBtn = widget.newButton{
label = "Save",
labelColor = { default={255}, over={255} },
font = native.systemFontBold,
xOffset=2, yOffset=-1,
default = "save-default.png",
over = "save-over.png",
width=60, height=30,
left=display.contentWidth-70, top=28
}

-- onRelease listener callback for saveBtn
local function onSaveRelease( event )
saveNote()
end
saveBtn.onRelease = onSaveRelease -- set as saveBtn's onRelease listener

-- display warning that will show at the bottom of screen
local warning = display.newImageRect( "warning.png", 300, 180 )
warning:setReferencePoint( display.BottomCenterReferencePoint )
warning.x = display.contentWidth * 0.5
warning.y = display.contentHeight - 28



2. Text Box and Text Field
두번째 단계는 텍스트 필드와 텍스트 박스 객체를 생성할 겁니다.
이 두 native text 위젯을 사용할 때 폰트를 지정해주기 위해서 native.newFont()를 어떻게 사용하는지 잘 봐 두세요.
그리고 텍스트 박스는 디폴트가 read only라는 것을 명심해 두시구요.
그래서 텍스트 박스에 유저가 문자를 입력할 수 있도록 하려면 isEditable 프로퍼티를 사용하셔야 합니다.

-------------------------------------------------------------------------------------
-- Create textFields

local textFont = native.newFont( native.systemFont )
local currentTop = sbHeight+tbHeight+shadow.contentHeight+10
local padding = 10

-- create textField
titleField = native.newTextField( padding, sbHeight+tbHeight+shadow.contentHeight+10, display.contentWidth-(padding*2), 28 )
titleField.font = textFont
titleField.size = 14

currentTop = currentTop + 28 + padding

-- create textBox
noteText = native.newTextBox( padding, currentTop, display.contentWidth-(padding*2), 264-currentTop-padding )
noteText.isEditable = true
noteText.font = textFont
noteText.size = 14



여기까지 하면 겉모습은 완성 된 겁니다.

다음은 외부 txt파일로 저장하고 불러오는 부분을 다룰 겁니다.

3. Saving and Loading
이 단계에서는 saving과 loading 기능을 넣을 겁니다. 이 기능들은 유저가 Save 버튼이나 Load 버튼을 누르면 생성하도록 하겠습니다.
그리고 최초에 앱이 시작될 때 loadSavedNote()함수가 불려져서 이전에 저장됐던 내용들이 display되도록 하겠습니다.

-------------------------------------------------------------------------------------
-- Saving and Loading functions

function loadSavedNote()
local title_path = system.pathForFile( "title.txt", system.DocumentsDirectory )
local note_path = system.pathForFile( "note.txt", system.DocumentsDirectory )
local fh_title = io.open( title_path, "r" )
local fh_note = io.open( note_path, "r" )

-- load the title
if fh_title then
titleField.text = fh_title:read()
io.close( fh_title )
end

-- load the note
if fh_note then
noteText.text = fh_note:read( "*a" ) -- '*a' is important to preserve line breaks
io.close( fh_note )
end
end

function saveNote()
local title_path = system.pathForFile( "title.txt", system.DocumentsDirectory )
local note_path = system.pathForFile( "note.txt", system.DocumentsDirectory )
local fh_title = io.open( title_path, "w+" )
local fh_note = io.open( note_path, "w+" )

-- load the title
if fh_title then
fh_title:write( titleField.text )
io.close( fh_title )
end

-- load the note
if fh_note then
fh_note:write( noteText.text )
io.close( fh_note )
end
end

loadSavedNote() -- on app start, load previously saved note



이 화면이 완성된 화면입니다.

아래 파일을 다운 받으시면 Full Souce Code와 이미지 파일 등이 있습니다. 참고하세요.

파일 받으시면서 추천버튼도 꾹 부탁드려요.
질문 있으시면 언제든지 댓글에 남겨 주세요.

반응형


반응형
아래 싸이트에 Corona SDK로 cross platform 앱 개발시 지켜야할 10계명이 올라왔더라구요.

http://fullycroisened.com/10-strategic-tips-for-cross-platform-development-using-corona-sdk/

읽어보니까 많이 공감이 되네요.

그 10가지를 아래 소개합니다.



Corona SDK로 cross platform 앱 개발시 주의해야 할 10가지

1. 실제 기기에서 초기부터 그리고 자주 테스트 하라. 에뮬레이터를 너무 믿지 마라. 실제 기기에서 테스트 하라.
2. 시작하기 전에 해상도에 맞는 이미지 사이즈들을 어떻게 다이나믹하게 다룰지를 먼저 계획하라.
   (각 해상도에 맞춘 이미지들을 미리 계획한다면 많은 시간을 절약할 수 있다.)
3. build.settings 파일에 어떤 퍼미션들을 넣어야 하는지 알고 있어야 한다.
예)
    androidPermissions =
    {
      "android.permission.INTERNET",
      "android.permission.VIBRATE",
      "android.permission.READ_PHONE_STATE",
      "android.permission.CALL_PHONE",
     },
4. 좀 더 테스트 하라.
5. 각 플랫폼에 대한 API들의 제한 사항들을 알고 있어라.
   어떤 기능들은 플랫폼에 따라 기능이 제한 돼 있다.
6. 코딩을 하기 전에 모든것을 Sketch 하라.
   (이렇게 하면 분명 많은 시간을 절약 할 수 있다.)
7. 메모리 사용량과 texture map size 를 항상 체크하라.  
   이렇게 함으로서 쓸데없이 메모리를 차지하는 경우 (이를 일찍 발견하게 돼) 메모리 컨트롤을 더 쉽게 할 수 있다.
8. 지금 하고 있는 것을 다른 곳에서는 어떻게 처리 했는지 research를 해 봐라. 그리고 나서 진행하면 도움이 된다.
   그리고 이것을 공유하라 그리고 당신이 어떻게 좀 더 낫게 구현했는지 살펴 봐라.
9. 커뮤니티를 활용하라. 거기에는 도움을 줄 아주 많은 소스들이 있다.
10. 즐겨라. 코딩이 제일 잘 되는 시간은 밤 11:34부터 새벽 3:07분까지이다.

==> 대부분 공감을 하는데 10번은 공감을 못하겠네요.
일은 일하는 시간에 해야지... 그래야 능률도 더 잘 오르고......
이러면 완전 폐인 되잖아....
초창기에는 나도 밤 새 코딩을 하기도 했는데...
이제는 근무시간에만 딱 하는게 제일 좋더라구요.
밤에 할거면 차라리 일찍 자고 새벽에 일어나서 하던지.....
그리고 6번은 좀 더 강조 하고 싶어요. 단순히 스케치를 하라가 아니고 제대로 된  스토리 보드 만들고 다이어 그램 만들고 클래스/객체 설계도 만들고 진행하라고요. 제대로 기획을 해야 코딩도 제대로 하죠. 앱 만들 때 중요도 기획 80% 코딩 20%.  (내 의견)


  가시기 전 여기 손가락 꾹 ~~~~ ^^        추천 추천...
반응형