使用phoneGap和Sencha Touch 2開發Android應用程序(三)

         本文是使用phoneGap和Sencha Touch 2開發Android應用程序」系列教程的第3章, 在這一章,咱們會建立編輯筆記的視圖,在這個視圖上,用戶可以建立,編輯以及刪除筆記。 數據庫

        在這一章結束後,本應用程序將可以建立新的筆記,對已有的筆記進行編輯。下面咱們開始建立編輯筆記視圖。 編程

在Sencha Touch中建立表單面板(Form Panel)

咱們在NoteEditor.js文件中編寫View的代碼 , NoteEditor.js文件創建在view目錄下: 瀏覽器

在NoteEditor.js文件中, 咱們將定義一個空的NoteEditor類,以下所示: 緩存

Ext.define("NotesApp.view.NoteEditor", {
    extend: "Ext.form.Panel",
    requires: "Ext.form.FieldSet",
    alias: "widget.noteeditor",
    config:{
        scrollable:'vertical'
    },
    initialize: function () {

        this.callParent(arguments);

    }
});

該Note Editor 繼承自Ext.form.Panel類。由於咱們須要在視圖中使用到一個FieldSet類的實例,因此須要聲明requires配置參數來告訴加載器下載該類的源碼。 一樣的,咱們使用scrollable配置參數來使面板的內容可以上下滾動。這樣當form表單的高度超過移動設備的屏幕高度時,纔不會出現佈局混亂。 app

在繼續完善NoteEditor的代碼以前,咱們先來回顧一下該視圖的構成: 框架

很容易發現,該視圖由3部分組成,從上至下依次爲一個包含了"返回"和「保存」按鈕的頂部Toolbar,一個包含了「標題」textfield和「內容」textareafield的Fieldset,以及一個包含了「刪除」按鈕的底部Toolbar。 dom

與編寫Notes ListContainer類同樣, 咱們也使用 initialize()方法來定義Note Editor所包含的的組件: 編輯器

Ext.define("NotesApp.view.NoteEditor", {
    extend: "Ext.form.Panel",
    requires: "Ext.form.FieldSet",
    alias: "widget.noteeditor",
    config:{
        scrollable:'vertical'
    },
    initialize: function () {


        this.callParent(arguments);


        var backButton = {
            xtype: "button",
            ui: "back",
            text: "返回"
        };


        var saveButton = {
            xtype: "button",
            ui: "action",
            text: "保存"
        };


        var topToolbar = {
            xtype: "toolbar",
            docked: "top",
            title: "編輯筆記",
            items: [
                backButton,
                { xtype: "spacer" },
                saveButton
            ]
        };


        var deleteButton = {
            xtype: "button",
            iconCls: "trash",
            iconMask: true,
            scope: this
        };


        var bottomToolbar = {
            xtype: "toolbar",
            docked: "bottom",
            items: [
                deleteButton
            ]
        };


        var noteTitleEditor = {
            xtype: 'textfield',
            name: 'title',
            label: '標題',
            required: true
        };


        var noteNarrativeEditor = {
            xtype: 'textareafield',
            name: 'narrative',
            label: '內容'
        };


        this.add([
            topToolbar,
            { xtype: "fieldset",
                items: [noteTitleEditor, noteNarrativeEditor]
            },
            bottomToolbar
        ]);
    }


});

         initialize()方法首先會調用 callParent()方法,而後依次定義包含「返回」和「保存」按鈕的頂部工具欄,和包含「刪除」按鈕的底部工具欄。 ide

         noteTitleEditor標題編輯器 和 noteNarrativeEditor內容編輯器被用來編輯筆記的標題和內容,它們分別是Ext.form.Text 和 Ext.form.TextArea組件的實例。 工具

        當全部的視圖組件都定義以後,接下來的工做就是將它們添加到View視圖上去。在這裏咱們使用了Ext.form.FieldSet組件來改善Form表單的外觀,標題和內容編輯器都將放置在FieldSet中顯示。

this.add([
    topToolbar,
    { xtype: "fieldset",
        items: [noteTitleEditor, noteNarrativeEditor]
    },
    bottomToolbar
]);

在Sencha Touch中顯示View視圖

        在開始編寫Note Editor的特性以前,咱們先編寫代碼,讓 Note List Container視圖中 「新建」筆記按鈕被按下時,該Note Editor視圖顯示出來。

        在以前的章節中,咱們爲「新建」按鈕建立過一個handler處理方法,當newNoteCommand 事件發生時,該方法會被調用。咱們還須要將 onNewNoteCommand() 監聽器添加到Notes Controller控制器中。這樣咱們就能使用該handler方法來激活Note Editor視圖。

        爲了在控制器中激活Note Editor視圖,咱們首先須要獲得這個視圖的引用,因此咱們定義了noteeditor 這個引用來達到目的。

Ext.define("NotesApp.controller.Notes", {

    extend: "Ext.app.Controller",
    config: {
        refs: {
            // We're going to lookup our views by xtype.
            notesListContainer: "noteslistcontainer",
            noteEditor: "noteeditor"
        },

   // Remainder of the controller’s code omitted for brevity.

});

        以前已經說過,定義以後,框架會在控制器中爲這個引用會自動建立getNoteEditor() 方法,使用這個方法,就能獲得視圖的引用而且在應用程序中激活視圖。

        接下來,咱們對onNewNoteCommand() 方法進行改寫:

onNewNoteCommand : function(){// 當 新增按鈕點擊時調用
 // alert("onNewNoteCommand");
 
 // 筆記建立時間及Id
 var now = new Date();
 var noteId = (now.getTime()).toString() + (this.getRandomInt(0,100)).toString();
 
 var newNote = Ext.create("NotesApp.model.Note",{
     id : noteId,
     dateCreated : now,
     title : "",
     narrative : ""
 });
 
 this.activateNoteEditor(newNote);
 }

        這裏建立了一個新的筆記,而且將它做爲參數傳給了activateNoteEditor() 方法。

        在這裏咱們使用了getRamdomInt() 輔助方法來生成筆記的惟一id:

getRandomInt: function (min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}

    activateNoteEditor() 方法的做用是將新建的筆記加載到Note Editor視圖中,而且激活視圖:

activateNoteEditor: function (record) {
    	var noteEditor = this.getNoteEditor();
	    noteEditor.setRecord(record); // load() is deprecated.
	    Ext.Viewport.animateActiveItem(noteEditor, this.slideLeftTransition);
	}

        這裏咱們使用了Ext.form.Panel的setRecord()方法,來將一個Model類的實例加載到Form表單中,這個Model實例的各個屬性將按照名稱來匹配並填充到Form中去。(注:這裏很容易理解,相似J2EE的編程方式,點擊新增按鈕跳轉到新建筆記的頁面中時,筆記的建立時間和id已經按照必定的規則生成完畢,新增頁面只須要展現標題和內容這兩個須要用戶輸入的表單元素)

        該方法用到了slideLeftTransition變量,咱們採用下面的代碼來定義它:

slideLeftTransition: { type: 'slide', direction: 'left' }

        使用這個transition變量會使得Note Editor視圖以從右到左滑動的方式來顯示出來。

        至此爲止,當點擊新建按鈕時,控制器所要作的工做(激活並展現Note Editor視圖)就完成了,緊接着咱們將在app.js中配置這個視圖,以便應用程序能識別它。

使應用程序可以識別視圖

        在app.js文件中,在views配置項中添加新建視圖:

views: ["NotesList", "NotesListContainer", "NoteEditor"]

        接着在應用程序的launch 方法中實例化該視圖:

Ext.application({
    name: "NotesApp",

    models: ["Note"],
    stores: ["Notes"],
    controllers: ["Notes"],
    views: ["NotesList", "NotesListContainer", "NoteEditor"],

    launch: function () {

        var notesListContainer = {
            xtype: "noteslistcontainer"
        };
        var noteEditor = {
            xtype: "noteeditor"
        };

        Ext.Viewport.add([notesListContainer,noteEditor]);
    }
});

        這時能夠啓動模擬器來看看運行效果。

        單擊新建按鈕時將顯示Note Editor視圖:

如何在Sencha Touch 列表中編輯一條記錄

        當單擊筆記列表中某條記錄右側的disclose按鈕圖標時,一樣須要激活到編輯視圖中:

        如今咱們來從新編寫控制器中的 onEditNoteCommand()方法實現這個功能:

onEditNoteCommand : function(list,record){
		// 當 編輯按鈕點擊時調用
		// alert("onEditNoteCommand");
		this.activateNoteEditor(record);
	}

        事實上實現這個功能很是簡單,由於disclose時間將被選中的Note對象實例經過record參數傳遞給了handler,而後咱們再次調用activateNoteEditor()來進入編輯筆記視圖。(注,看看咱們以前編寫的NoteListContainer視圖裏面的代碼):

var notesList = {
			xtype:"noteslist",
			store:Ext.getStore("Notes"),
			listeners:{
				disclose:{
					fn:this.onNotesListDisclose,
					scope:this
				}
			}
		}
        從這裏咱們能夠看到,在noteList列表定義的時候,咱們就爲他添加了一個監聽器,用來監聽disclose動做,並把處理的權力交給了onNotesListDisclose 方法,
onNotesListDisclose:function(list,record,target,index,evt,options){
		// alert("editNoteCommand");
		this.fireEvent("editNoteCommand",this,record);
	}
        這個onNotesListDisclose 方法能夠接收一系列跟列表相關的參數,在咱們這裏,只須要傳遞list對象和被選中的記錄就好。

        鏈接好手機,或者啓動模擬器,就能看到選中記錄的編輯視圖了:

        完成了筆記的新增和編輯,接下來咱們實現保存的代碼,在這以前,咱們先理一下保存的思路。

使用Sencha Touch 的本地存儲代理(LocalStorage Proxy)

        首先,咱們須要對以前章節採用的硬編碼形式存儲的筆記對象的代碼進行改造。

Ext.define("NotesApp.store.Notes",{
	extend:"Ext.data.Store",
	config:{
		model:"NotesApp.model.Note",
		data:[
		      {title:"Note 1", narrative:"這是Note 1 的筆記內容"},
		      {title:"Note 2", narrative:"這是Note 2 的筆記內容"},
		      {title:"Note 3", narrative:"這是Note 3 的筆記內容"},
		      {title:"Note 4", narrative:"這是Note 4 的筆記內容"},
		      {title:"Note 5", narrative:"這是Note 5 的筆記內容"},
		      {title:"Note 6", narrative:"這是Note 6 的筆記內容"},
		],
		sorters:[{property:'dateCreated',direction:'DESC'}]
	}
});

        咱們使用LocalStorage Proxy的方式來將應用程序運行時添加的筆記緩存到設備上:

Ext.define("NotesApp.store.Notes",{
	extend:"Ext.data.Store",
	requires:"Ext.data.proxy.LocalStorage",
	config:{
		model:"NotesApp.model.Note",
		proxy : {
			type : "localstorage",
			id : "notes-app-store"
		},
		sorters:[{property:'dateCreated',direction:'DESC'}]
	}
});

            LocalStorageProxy採用了 HTML5 的 localStorage API 來將Model對象數據 保存在客戶端瀏覽器中。這個代理類能夠存儲一些相同的數據記錄。這個代理類須要一個id配置參數,用來標識不一樣的本地數據對象。

        這樣咱們就可以從本地數據庫中保存和讀取筆記了,如今咱們回到視圖和控制器來完善保存筆記的功能。

如何在Sencha Touch控制器中添加事件監聽

        在NoteEditor 視圖類中,修改initialize() 方法中的saveButton組件定義:

var saveButton = {
        xtype: "button",
        ui: "action",
        text: "保存",
        handler:this.onSaveButtonTap,
	scope:this
   }

        經過handler配置參數定義了一個處理方法,當用戶點擊按鈕時,該方法就會運行。另外還配置了scope參數值this來保證在本NoteEditor視圖中運行該方法,即聲明該方法的做用域只限於該視圖。

        接下來定義onSaveButtonTap() 方法:

onSaveButtonTap:function(){
	//alert("save note");
	this.fireEvent("saveNoteCommand",this);
}

        和以前編寫的事件處理方法相似,咱們經過給視圖定義saveNoteCommand事件來捕獲按鈕點擊的動做。

        事實上,若是NoteEditor視圖僅僅只是將這個saveNoteCommand事件傳播(fireEvent)出去的話,控制器並不可以監聽到這一事件。咱們須要明確的告訴控制器事件是從哪裏來的,因此咱們須要給控制器的refs配置參數加上noteEditor視圖。

refs: {
        // We're going to lookup our views by xtype.
        notesListContainer: "noteslistcontainer",
        noteEditor: "noteeditor"
   }

        而後,使用control配置參數來給saveNoteCommand這一事件映射一個對應的處理方法。

control: {
            notesListContainer: {
                // The commands fired by the notes list container.
                newNoteCommand: "onNewNoteCommand",
                editNoteCommand: "onEditNoteCommand"
            },
            noteEditor: {
                // The commands fired by the note editor.
                saveNoteCommand: "onSaveNoteCommand"
            }

        }

        最後,編寫onSaveNoteCommand() 方法來處理保存動做:

onSaveNoteCommand: function () {
       // console.log("onSaveNoteCommand");

        var noteEditor = this.getNoteEditor(); // 獲得編輯視圖
        var currentNote = noteEditor.getRecord();// 當前筆記
        var newValues = noteEditor.getValues();// 當前表單中所輸入的筆記屬性

        // 用表單數據來更新筆記
        currentNote.set("title", newValues.title);
        currentNote.set("narrative", newValues.narrative);

        var errors = currentNote.validate(); // 驗證

        if (!errors.isValid()) {// 驗證不經過,提示錯誤信息
            Ext.Msg.alert('警告!', errors.getByField("title")[0].getMessage(), Ext.emptyFn);
            currentNote.reject();
            return;
        }

        var notesStore = Ext.getStore("Notes");
	    // 經過id查找修改的筆記記錄,並添加到本地數據庫中
        if (null == notesStore.findRecord('id', currentNote.data.id)) {
            notesStore.add(currentNote);
        }

        notesStore.sync();// 數據庫記錄同步
		// 對同步後的記錄從新排序
        notesStore.sort([{ property: 'dateCreated', direction: 'DESC'}]);

        this.activateNotesList();
    }

        在onSaveNoteCommand() 方法中,首先須要獲得被編輯筆記的引用和編輯表單的值:

var noteEditor = this.getNoteEditor();
var currentNote = noteEditor.getRecord();
var newValues = noteEditor.getValues();

        而後,將新的屬性值設置到筆記中:

currentNote.set("title", newValues.title);
currentNote.set("narrative", newValues.narrative);

 Sencha Touch的數據模型驗證

        接下來是很是重要的一步,咱們須要對錶單數據進行驗證。首先,對Model對象調用validate() 方法,而後對返回的errors對象調用isValid() 方法進行驗證:

var errors = currentNote.validate(); // 驗證

        if (!errors.isValid()) {// 驗證不經過,提示錯誤信息
            Ext.Msg.alert('警告!', errors.getByField("title")[0].getMessage(), Ext.emptyFn);
            currentNote.reject();
            return;
        }

        Ext.data.Model類的validate() 方法會遍歷運行全部定義在Model類中的驗證器 (validations), 而後返回一個Ext.data.Errors類的實例對象,Errors實例對象包含了一組Ext.data.Error實例對象,在這組對象中,包含了每一個驗證的Model屬性對應的一個Error實例 。

Ext.define("NotesApp.model.Note",{
	extend:"Ext.data.Model",
	config:{
		idProperty:"id",
		fields:[
		   {name:"id",type:"int"},
		   {name:"dateCreated",type:"date",dateFormat:"c"},
		   {name:"title",type:"string"},
		   {name:"narrative",type:"string"}
		],
		validations:[
		    {field:"id",type:"presence"},
		    {field:"dateCreated",type:"presence"},
		    {field:"title",type:"presence",message:"請輸入標題"}
		]
	}
});

       咱們的應用程序只給筆記的標題綁定了一個驗證器。若是驗證不經過,將在屏幕上彈出一個提示窗口,提示信息的內容就是在Model裏驗證器配置的message,而後,會調用model的 reject()方法,這個方法使得被修改的表單元素恢復成原始值。

使用本地數據庫保存數據

        在 onSaveNoteCommand()方法中, 若是表單元素驗證經過,就須要把數據保存到設備的本地數據庫中:

var notesStore = Ext.getStore("Notes");
	    // 經過id查找修改的筆記記錄,並添加到本地數據庫中
        if (null == notesStore.findRecord('id', currentNote.data.id)) {
            notesStore.add(currentNote);
        }

        由於咱們對新增和編輯使用了同一個視圖,因此在保存以前,咱們須要經過findRecrod()方法來查找本地數據庫中是否存在這條記錄,若是不存在,就把該筆記添加到本地數據庫中。

        Store對象的sync() 用來同步數據,在本地數據庫中執行新增,編輯或者刪除操做。

        本地數據更新以後,從新對數據進行排序處理:

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

返回主視圖

        在onSaveNoteCommand ()方法的最後,調用了activateNotesList() 方法,和activateNoteEditor()方法相似,這個方法使得應用程序跳轉到主視圖,即列表視圖:

activateNotesList: function () {
    Ext.Viewport.animateActiveItem(this.getNotesListContainer(), this.slideRightTransition);
}

        返回主視圖時,這回咱們使用定義的一個右滑:

slideRightTransition: { type: 'slide', direction: 'right' }
        再次運行應用程序,保存筆記:

總結

        在這一章,咱們完成了本應用程序的一些新功能,好比建立新的筆記,編輯已有的筆記等。

        咱們學習瞭如何使用Sencha Touch的表單組件來編輯應用程序中的數據,以及在應用程序運行時,如何使用本地存儲代理的實例來把數據緩存到本地設備。還學習瞭如何驗證Sencha Touch的數據模型,如何將數據同步存儲到本地數據庫中,以及在數據驗證失敗時恢復數據改變。

        另外,咱們還學習了在擁有多個視圖的應用程序中,如何建立不一樣視圖切換時的滑動效果。

        到此爲止,咱們的應用程序還缺乏刪除筆記的功能。咱們將在下一章添加該功能,同時,咱們將在下一章對筆記列表進行修改,讓列表可以按照日期來分組顯示。

        未完待續!

下載連接

        源代碼已發佈到迅雷快傳:http://kuai.xunlei.com/d/KLBGTSXIGTJA

        原文出自: http://miamicoder.com/2012/how-to-create-a-sencha-touch-2-app-part-3/

本教程快速連接

相關文章
相關標籤/搜索