FB LIKE JACKER

《Enyo框架之旅》第六部分:程序选项

2011 年Apr月 6 日由 发布 | 类别: Enyo开发, 实用webOS开发教程, 开发者社区 | Tags: , , , ,
第六部分:程序选项

接下来,我们要对程序做一些定制的工作。我们要给用户提供一个选项的菜单,供用户定制一些选项。首先,让我们做一个修改默认feed地址的选项。

我们将创建一个Preferences.js文件,其中存放了用户偏好信息,和简单的选项页面。以下是文件的内容:

enyo.kind({
	name: "MyApps.Preferences",kind: enyo.VFlexBox,
	      events: {
	      	      onReceive: "",
		      onSave: "",
		      onCancel: ""
	      },
	components: [
		    {kind: "PageHeader", content: "Enyo FeedReader - Preferences"},
		    {name: "preferencesService", kind: "enyo.SystemService"},
		    {kind: "VFlexBox",
		    	   components: [
			   	       {kind: "RowGroup", caption: "Default Feed", components: [
				       	      {name: "defaultFeedInput", kind: "FancyInput"}
				       ]},
		    		       {kind: "HFlexBox", pack: "end", style: "padding: 0 10px;",
		    	   	       	      components: [
			   	       	      		 {name: "saveButton", kind: "Button",content: "Save", onclick: "saveClick"},
				       			 {width: "10px"},
				       			 {name: "cancelButton", kind: "Button",content: "Cancel", onclick: "cancelClick"}
			   		      ]
		    		       }
			    ]
		    },
	],
	create: function() {
		this.inherited(arguments);
		this.$.preferencesService.call(
			{
				keys: ["defaultFeed"]
			},
			{
				method: "getPreferences",
				onSuccess: "gotPreferences",
				onFailure: "gotPreferencesFailure"
			});

//保持更新,确保最新的值正确保存到服务端。

		this.savedUrl = "";
	},
	gotPreferences: function(inSender, inResponse) {
			this.savedUrl = inResponse.defaultFeed;
			this.$.defaultFeedInput.setValue(this.savedUrl);
			this.doReceive(this.savedUrl);
	},
	gotPreferencesFailure: function(inSender, inResponse) {
			       console.log("got failure from preferencesService");
	},
	showingChanged: function() {

//将最后输入的文本保存

	   this.$.defaultFeedInput.setValue(this.savedUrl);
	},
	saveClick: function(inSender, inEvent) {
		   var newDefaultFeedValue = this.$.defaultFeedInput.getValue();
		   this.$.preferencesService.call(
			{
				keys: {
				      "defaultFeed": newDefaultFeedValue
				}
			},
			{
				method: "setPreferences"
			});
		   this.savedUrl = newDefaultFeedValue;
		   this.doSave(newDefaultFeedValue);
	},
	cancelClick: function() {
		     this.doCancel();
	}
});

让我们分析一下以上代码。MyApps.Preferences对象是属于VFlexBox类的。有三个新时间被建立,其中两个——onSave和onCancel是通过点击点击保存、取消按钮触发的。onReceive事件是当我们保存一个选项数据时,从服务端收到成功的响应时触发的事件。程序从PageHeader开始,我们在FeedReader.js中见过这个组件,在这里使用它的原因是可以让我们更好的定制程序界面的基本样式。

接下来是定义preferencesService,它是SystemService类型的对象。当我们要保存或检索程序的自定义数据时,我们会调用preferencesService.call方法来完成。

接下来,是用户界面的元素。首先是一个RowGroup容器对象,其中包含FancyInput类型的输入控件,接收用户输入的不同的feed地址。接着是一个HFlexBox容器对象,里面包含保存和取消两个按钮。HFlexBox对象有一个样式项目(“style”),其中定义的意义是容器内内容之间和容器右边缘有10个像素的间距。还有一个pack参数,它的值是“end”。由于HFlexBox是一个水平容器,此参数告诉其中的内容从尾部(end)开始,即右对齐样式。

  • 代码剩余部分如下:

 

  • 创建一个gotPreferences方法,用来与preferencesServices通信。并且创建一个(this.savedUrl)变量,用来存放通信的参数。
  • 如果gotPreferences调用getPreferences方法成功返回,this.savedUrl将返回以前存储的默认feed地址,然后将它更新到feed地址输入框中。最后,成功返回触发onReceive事件,onReceive去处理FeedReader。
  • FeedReader方法在getPreference失败的时候,记录失败日志消息,并调用系统处理。
  • 每当feed地址每次改变时showingChanged方法就被自动调用,它将当时this.savedUrl中的内容保存到Service中。
  • saveClick方法是保存feed的大部分的内容。当保存方法被按下,saveClick进程将文本框内的内容读出,并使用preferencesService.call方法设置Services中的值。(请注意,我们指定defaultFeed的一种“key”类型存放数据到Service中。)保存按钮更新this.savedUrl的值,其后,触发onPrefsSave事件,交由FeedReader处理。
  • 点击取消按钮触发onCancel事件。这个事件同样也是交由FeedReader对象处理。

这时,我们看到下面的截图:

 

 

现在,让我们看一下FeedReader.js文件,对代码做了一些更改,不仅仅局限于添加onSave,onCancel以及onReceive等事件。代码如下:

components: [
	    {name: "pane", kind: "Pane", flex: 1,
	    	   components: [
		   	       {name: "search", className: "enyo-bg", kind: "MyApps.Search",
			       	      onSelect: "feedSelected", onLinkClick: "linkClicked"},
			       {name: "detail", className: "enyo-bg", kind: "MyApps.Detail"},
			       {
				name: "preferences",
				className: "enyo-bg",
				kind: "MyApps.Preferences",
				onReceive: "preferencesReceived",
				onSave: "preferencesSaved",
				onCancel: "preferencesCanceled"
			       }
	     ]},
	     {kind: "AppMenu",
	     	    components: [
		    		{caption: "Preferences", onclick: "showPreferences"},
				]
	     }
]

注意到以上代码出现了Detail.js取代了原来的具体视图页面。其中定义了MyApps.Detail类。我们将会在后面仔细分析Detail视图。现在,我们先将FeedReader.js剩下的代码分析完毕。在FeedReader的主显示面板上,我们声明了MyApps.Detail类的新视图(”preferences”)。在此声明中,你可以找到三个处理Preferences.js文件传递参数的事件。

在FeedReader.js中,通过一个AppMenu对象将Preferences视图接入到程序中,显示一个菜单。当点击菜单时,触发一个行的方法FeedReader.showPreferences,此方法显示选项视图。

在浏览器中查看AppMenu如下图:

 

 

(注意,在浏览器中你可以使用快捷键 CTRL + `唤出菜单,点击菜单外任何位置,退出菜单。)

接下来,让我们看看新的FeedReader.js中包含的函数。

openAppMenuHandler: function() {
		    this.$.appMenu.open();
},
closeAppMenuHandler: function() {
		     this.$.appMenu.close();
},
feedSelected: function(inSender, inFeed) {
	      this.$.pane.selectViewByName("detail");
	      this.$.detail.setUrl(inFeed.link);
},
linkClicked: function(inSender, inUrl) {
	     this.$.detail.setUrl(inUrl);
	     this.$.pane.selectViewByName("detail");
},
showPreferences: function() {
		 this.$.pane.selectViewByName("preferences");
},
preferencesReceived: function(inSender, inDefaultUrl) {
		     this.$.search.setFeedUrl(inDefaultUrl);
},
preferencesSaved: function(inSender, inFeedUrl) {
		  this.$.search.setFeedUrl(inFeedUrl);
		  this.$.pane.back();
},
preferencesCanceled: function(inSender) {
		     this.$.pane.back();
},
backHandler: function(inSender, inEvent) {
	     this.$.pane.back(inEvent);
}

当强的FeedReader.js中包含9个函数,之前的文件只包含5个函数,其一,backHandler不变直接拿来用。其中的feedSelected和linkClicked稍稍修改了一下(现在调用setUrl的this.$.detail替代了过去的this.$.webView,因为webView被移动到Detail.js中去了。)剩下的两个方法create和viewSelected被删除。这两个函数实现的功能被转移到其他对象中实现。

  • 现在让我们研究一下新加的这些方法。
  • openAppMenuHander和closeAppMenuHandler是比较简单的程序。我们使用这两个方法控制AppMenu菜单的显示和关闭。我们没有具体定义这两个方法的具体实现,它会根据enyo框架类继承的接口来处理菜单的操作。
  • showPreferences方法也比较容易,它实现当用户点击“选项”条目时,显示preferences界面。
  • preferencesReceived是对Preferences.js中的onReceive事件的响应和处理程序。当选项视图中保存默认feed地址成功之后,preferencesReceived方法返回默认feed地址给search视图,所以,它可以更新文本输入框的内容。
  • preferencesSaved是对Preferences.js中的onSave事件的响应和处理程序。当用户保存了feed的地址之后(类似preferencesReceived的处理流程),传递新的feed值给Search视图,并退出preferences界面。
  • preferencesCanceled是对Preferences.js中的onCancel事件的响应和处理程序。它直接退出preferences界面。

现在让我们来看一下Detail.js的代码。虽然是一个新文件,但是代码看起来多少会有点眼熟的。

enyo.kind({
	name: "MyApps.Detail",
	kind: "VFlexBox",
	published: {
		   url: ""
	},
	components: [
		    {kind: "PageHeader", content: "Enyo FeedReader"},
		    {kind: "Scroller", flex: 1, components: [
		    	   {name: "webView", kind: "WebView", className: "enyo-view"}
		    ]}
	],
	showingChanged: function() {
			if (!this.showing) {
			   this.$.webView.setUrl("");
			}
	},
	urlChanged: function() {
		    this.$.webView.setUrl(this.url);
	}
});

这里需要注意的一点是,“url”这个公共数据对象和跟它关联的urlChanged方法。

公共数据对象的数据是由外部对象来访问的。Published属性被定义在一种特殊的“Published”块上。它的格式是在名称之后紧接一个冒号然后是引号中的默认值。如果你回头看FeedReader.linkClicked方法,你会发现它包含一个Detail.setUrl调用。当类型被声明为published是,enyo框架自动为它生成getter和setter方法。例如这里,被命名为getUrl和setUrl的方法。

此外,如果你希望当published类型的值改变时,自动触发一些事件的话(例如对象的setter方法成功调用时)enyo框架使得你可以通过修改属性来调用方法。在我们的例子中恰好有一个叫做urlChanged的方法。所以当 FeedReader.linkClicked调用Detail.setUrl时Detail对象会发生下面的过程。

  1. this.url对象的值会从FeedReader对象中获取url的值。
  2. 因为我们定义了一个urlChanged方法,所以this.urlChanged会被自动调用。

 

同样值得一提的是showingChanged方法。我们看到以前在Preferences.js中实现showingChanged方法,并指出enyo框架会在对象的可见性属性改变时自动调用对应的处理方法。在这里Detail.showingChanged的过程中,我们又发现对象是否可见可以通过修改他的属性值来实现。

不要忘了修改我们的depends.js文件,因为我们添加了Detail.js和Preferences.js两个文件。

代码如下:

enyo.depends(
	"source/FeedReader.js",
	"source/Search.js",
	"source/Detail.js",
	"source/Preferences.js",
	"css/FeedReader.css"
);

最后,我们对Search.js做一些小的改动。

像 Preferences.js和Detail.js一样Search.js要在显示元素的顶部包含一个PageHeader元素:

{kind: "PageHeader", content: "Enyo FeedReader"}

FancyInput现在有一个名字,叫做”feedUrl”。

{kind: "RowGroup", caption: "Feed URL", components: [
       {name: "feedUrl", kind: "FancyInput",
       	      value: “http://feeds.bbci.co.uk/news/rss.xml",
	      	     components: [
		     		 {kind: "Button", caption: "Get Feed", onclick: "btnClick"}
				 ]
	}
]}

并且,btnClick方法已经改成this.$.feedUrl与this.$.feedUrl对应。

btnClick: function() {
	  var url = "http://query.yahooapis.com/v1/public/yql?q="
	      + "select%20title%2C%20description%2C%20link"
	      + "%20from%20rss%20where%20url%3D%22" + this.$.feedUrl.getValue()
	      + "%22&format=json&callback=";
	  this.$.getFeed.setUrl(url);
	  this.$.getFeed.call();
}

我们还修改了一个更简单的设置this.$.feedUrl内容的新方法。

setFeedUrl: function(inUrl) {
	    if (inUrl) {
	       this.$.feedUrl.setValue(inUrl);
	    }
}

使用模拟数据

你会发觉,enyo可以让我们在一个基于webkit的本地浏览器环境下做这么多的事情。其实,我们接着使用我们的浏览器环境开发程序环境,如大家所见,这种环境非常有利于对UI进行设计。当然,我们还是可以用它来做一些更低级的任务。例如基于低级web的通信。

即使是这样,但是基于enyo框架的程序在设备上提供的资源也是有限的。即使是访问PalmService类型的服务,或者用来访问设备端的用户数据库的用户数据,都无法做到。那是由于基于浏览器的环境不能为用户提供更多的数据存放的空间。为了弥补这一点不足,enyo框架模拟了一种从PalmService服务提供返回数据的机制。这是利用一个名为MockPalmService的服务做到的。这个服务是利用一个自己创建的静态数据文件。这个文件存在于名为mock的程序子文件夹中。并且,他的名字是GRANDPARENT_PARENT_SERVICE.json。每当一个在浏览器中的程序调用PalmService服务时,enyo就会自动寻找在这个位置的此文件进行操作。

在此教程应用程序中,为了测试的目的,我们已经在Preferences.js文件中建立了一个名为preferencesService的模拟数据服务。本教程例子在FeedReader/mock/目录下的FeedReader_preferences_preferencesService.json文件。

以下是feedReader_preferences_preferencesService.json文件的内容:

{
"defaultFeed": "Your default feed here",
"returnValue": true
}

请注意,returnValue属性必须设置为true用来保证调用的数据可以被返回。

还要注意的是,数据文件只能被读取,不能被写入。

 

 

最后要说的:感谢大家与我们一同学习enyo框架,在这个工程中大家也许学到不少东西。要获得更多的关于enyo框架的知识,需要阅读enyo的开发文档了。通过阅读文档可以可以了解到跟多的东西。诸如,enyo是如何工作的,以及为什么需要使用enyo框架等等问题。

同时也别忘了阅读enyo API文档,其直接来源于源代码。(包括字面和引申意义)

对于那些想一边学习一边动手作的人来说,以下是一些对本程序添加功能的建议,大家可以尝试着自己动手试试:

  1. 添加跟好的数据处理代码,使他可以显示图片格式的数据对象。
  2. 为用户喜欢的feed添加书签功能。
  3. 为用户界面添加不同的皮肤。

 

或者,创建一个全新的应用程序。祝大家:财源广进,累坏惠普。

enyo开发者,翻译者,尊上。

本教程的PDF下载地址:http://ge.tt/4cX2zwC

声明:此教程有翻译不得当的地方欢迎指出!不吝赐教!
开发环境的构架请参照:搭建Enyo开发环境的教程感谢@911boyV
前几节可以到:enyo开发栏目去查看
锋客网phonekr出品
锋客网开发小组组员crubbish4
« 一款你会超爱的天气补丁:Statusbar Weather Widget
[2.3.3 for HTC Hero] Elelinux-1.7.1-cnrgb.com 极智网一步到位版 »

About webOS新闻组

锋客网创始人,90后一名.接触webOS的时间很短,但是喜欢的也特别深,现用机型Pixi+.就一句了?翻滚吧!webOS!

» has written 102 posts

锋客的朋友们

  • 少数派
  • 煮机网

签订契约成为机油吧!

Buy me a coffee~ ;-)

Buy me a coffee~ ;-)
閃開│讓專業的來 沒辦法│我這個人就是太正直了