前言:
视图[View]是MVC应用程序的脸面,不管你的应用程序设计如何,用户都只能看到眼前的视图,因此对你的评价也只能通过对视图的体验来得到。因此无论如何,一定要用心去设计你的视图。
这篇文章的英文原址是http://docs.sencha.com/touch/2-0/#!/guide/views
原文标题是:Using Views in your Applications(在应用程序中使用视图)。在官方文档目录中,它事实上的地位是MVC概述之后开篇三板斧之一,鉴于Sencha Touch MVC的特点,这三板斧的介绍顺序是倒过来的,先C控制器再V视图最后才是M数据模型,但是其重要性却不分先后。
Sencha Touch 交流QQ群213119459欢迎您的加入。
Using Views in your Applications
在应用程序中使用视图
注:为方便起见,文中所有出现 Sencha Touch 的地方均以 ST 简写替代。
From the user's point of view, your application is just a collection of views. Although much of the value of the app is in the Models and Controllers, the Views are what the user interacts with directly. In this guide we're going to look at how to create the views that build your application.
从用户的视角来看,你的应用程序就是一堆视图的集合,尽管应用程序大部分有价值的东西都在数据模型和控制器里,但视图才是直接跟用户互动的东西。这篇指南将带我们学习如何创建视图来搭建应用程序。
Using Existing Components
使用现存组件
The easiest way to create a view is to just use Ext.create with an existing Component. For example, if we wanted to create a simple Panel with some HTML inside we can just do this:
最简单的方法是使用Ext.create来创建一个ST中现存的内置组件,这同样也是一个视图。例如我们只想要创建一个包含简单html代码的Panel,我们可以这么做:
Ext.create('Ext.Panel', {
html: 'Welcome to my app',
fullscreen: true
});
This simply creates a Panel with some html and makes it fill the screen. You can create any of our built-in components this way but best practice is to create a subclass with your specializations and then create that. Thankfully that's simple too:
上述例子将会创建一个包含html代码的Panel并使其充满屏幕。你可以用这种方法创建任意内置组件,不过最好的方式还是专门创建一个(内置组件的)子类,然后再来创建这个(定制后的)子类的实例。幸好这也不是什么麻烦事儿。
Ext.define('MyApp.view.Welcome', {
extend: 'Ext.Panel',
config: {
html: 'Welcome to my app',
fullscreen: true
}
});
Ext.create('MyApp.view.Welcome');
The outcome is the same, but now we have a brand new component that we can create any number of times. This is the pattern we'll normally want to follow when building our app - create a subclass of an existing component then create an instance of it later. Let's take a look through what we just changed:
上述代码的结果是一样的,但是我们现在有了一个自己命名的新组件,而且以后我们还可以多次使用这个新组件。这才是我们推荐的应用开发模式:创建一个现存组件的子类然后再创建该子类的实例。我们来看一下这种方式跟前面相比有哪些变化:
Any of the config options available on Ext.Panel can now be specified in either our new class's config block or when we come to create our instance. When defining a subclass be sure to use the config object, when creating just pass in an object.
此时Ext.Panel的所有有效config属性都可以在新类的config配置块中进行定义或者在创建实例时传入使用了。定义子类的时候务必使用config对象(其实就是用{}包裹的一系列配置参数的集合),创建的时候也可以传入一个config对象集合。
For example, here's the same code again but with additional configuration passed in with our Ext.create call:
下面的例子只在刚才代码基础之上,在Ext.create时传入了一个新的配置参数。
Ext.define('MyApp.view.Welcome', {
extend: 'Ext.Panel',
config: {
html: 'Welcome to my app',
fullscreen: true
}
});
Ext.create('MyApp.view.Welcome', {
styleHtmlContent: true
});
A Real World Example
一个更贴近现实的例子
This is one of the view classes from our Twitter app:
这个视图类来自于我们的Twitter应用:
Ext.define('Twitter.view.SearchBar', {
extend: 'Ext.Toolbar',
xtype : 'searchbar',
requires: ['Ext.field.Search'],
config: {
ui: 'searchbar',
layout: 'vbox',
cls: 'big',
items: [
{
xtype: 'title',
title: 'Twitter Search'
},
{
xtype: 'searchfield',
placeHolder: 'Search...'
}
]
}
});
This follows the same pattern as before - we've created a new class called Twitter.view.SearchBar, which extends the framework's Ext.Toolbar class. We also passed in some configuration options, including a layout and an items array.
它遵循前面提到的规则,创建了一个继承框架Ext.Toolbar类的新子类,命名为Twitter.view.SearchBar,我们给它传入一些配置参数,其中包含了一个layout属性和items数组。
We've used a couple of new options this time:
这次我们使用了两个新的参数:
This allows us to create instances of our new view class in a couple of ways:
以后我们就可以通过两种方式来创建新视图(Twitter.view.SearchBar)的实例了。
//creates a standalone instance
//创建一个独立的实例
Ext.create('Twitter.view.SearchBar');
//alternatively, use xtype to create our new class inside a Panel
//或者我们在一个Panel中通过xtype方式来创建它
Ext.create('Ext.Panel', {
html: 'Welcome to my app',
items: [
{
xtype: 'searchbar',
docked: 'top'
}
]
});
Custom Configurations and Behavior
自定义配置和行为
Sencha Touch 2 makes extensive use of the configuration system to provide predictable APIs and keep the code clean and easily testable. We strongly suggest you do the same in your own classes.
ST2广泛应用了configuration系统来提供可控性好的API并保持代码简洁和容易测试。我们强烈建议你在自己的类里面也这么做。
Let's say you want to create a image viewer that pops up information about the image when you tap on it. Our aim is to create a reusable view that can be configured with the image url, title and description, and displays the title and description when you tap on it.
假设你要创建一个图片浏览视图,当它被点击的时候会弹出该图片的信息。那么我们当然希望这个视图有很好的重用性,你可以配置图片url、标题、描述属性,并且当点触的时候会自动把标题和描述弹出来。
Most of the work around displaying images is taken care of for us by the Ext.Img component, so we'll subclass that:
Ext.Img组件已经围绕图片展示功能做了大量的工作,因此我们继承它既可:
Ext.define('MyApp.view.Image', {
extend: 'Ext.Img',
config: {
title: null,
description: null
},
//sets up our tap event listener
//设置点击事件监听器
initialize: function() {
this.callParent(arguments);
this.element.on('tap', this.onTap, this);
},
//this is called whenever you tap on the image
//每当你点触屏幕的时候就会调用这个函数
onTap: function() {
Ext.Msg.alert(this.getTitle(), this.getDescription());
}
});
//creates a full screen tappable image
//创建一个全屏的可点击的图片
Ext.create('MyApp.view.Image', {
title: 'Orion Nebula',
description: 'The Orion Nebula is rather pretty',
src: 'http://apod.nasa.gov/apod/image/1202/oriondeep_andreo_960.jpg',
fullscreen: true
});
We're adding two more configurations to our class - title and description - which both start off as null. When we create an instance of our new class we pass the title and description configs in just like any other configuration.
我们给自己的类增加了两个新的配置参数:title和description,初始都默认为null。当我们创建这个新类的实例时,我们可以像传递其他参数一样把这两个参数传递进去。
Our new behavior happens in the initialize and onTap functions. The initialize function is called whenever any component is instantiated, so is a great place to set up behavior like event listeners. The first thing we do is use this.callParent(arguments) to make sure the superclass' initialize function is called. This is very important, omitting this line may cause your components not to behave correctly.
新定义的动作在初始化和点击的时候发生,initialize函数每当组件实例化的时候都会被调用,所以这个地方用来设置事件侦听器是最合适的。在这里我们做的第一件事是使用this.callParent(arguments)来确保父类的初始化函数已经被执行。这很重要,忽略这一步可能会导致你的组件无法正确的工作。
After callParent, we add a tap listener to the component's element, which will call our onTap function whenever the element is tapped. All components in Sencha Touch 2 have an element property that you can use in this way to listen to events on the DOM objects, add or remove styling, or do anything else you'd normally do to an Ext.dom.Element.
在callParent之后,我们给组件的element增加了一个tap侦听器,当这个element被点击时,onTap函数就会被调用。ST2的每一个组件都有一个element属性,通过它你可以侦听Dom对象的事件,增删样式,或者做其他你以往用Ext.dom.Element来做的事情。
The onTap function itself is pretty simple, it just uses Ext.Msg.alert to quickly pop up some information about the image. Note that our two new configs - title and description - both receivegenerated getter functions (getTitle and getDescription respectively), as well as generated setter functions (setTitle and setDescription).
onTap函数本身非常简单,它只是使用Ext.Msg.alert来快速弹出一些关于图片的信息。注意我们的两个新参数title和description都自动获得了其对应的getter方法(getTitle和getDescription),同样还有setter方法(setTitle和setDescription)。
Advanced Configurations
高级配置
When you create a new configuration option to a class, the getter and setter functions are generated for you so a config called 'border' is automatically given getBorder and setBorder functions:
当你为类创建一个新的配置参数时,一对getter和setter方法也会被生成,因此一个叫做border的配置参数会自动被给予getBorder和setBorder方法。
Ext.define('MyApp.view.MyView', {
extend: 'Ext.Panel',
config: {
border: 10
}
});
var view = Ext.create('MyApp.view.MyView');
alert(view.getBorder()); //alerts 10(弹出10)
view.setBorder(15);
alert(view.getBorder()); //now alerts 15(弹出15)
The getter and setter aren't the only functions that are generated, there are a couple more that make life as a component author much simpler - applyBorder and updateBorder:
除了getter和setter之外还会生成一对让开发者感到很爽的方法,applyBorder和updateBorder:
Ext.define('MyApp.view.MyView', {
extend: 'Ext.Panel',
config: {
border: 0
},
applyBorder: function(value) {
return value + "px solid red";
},
updateBorder: function(newValue, oldValue) {
this.element.setStyle('border', newValue);
}
});
Our applyBorder function is called internally any time the border configuration is set or changed, including when the component is first instantiated. This is the best place to put any code that transforms an input value. In this case we're going to take the border width passed in an return a CSS border specification string.
applyBorder函数会在每次border参数设置或者改变的时候被调用,包括组件第一次被实例化时。因此这个地方很适合放置改变参数值格式的代码,在这个例子里我们可以把传入的border宽度值变成css字符串格式。
This means that when we set the view's border config to 10, our applyBorder function will make sure that we transform that value to '10px solid red'. The apply function is totally optional but note that you must return a value from it or nothing will happen.
这意味着当我把视图的border参数设为10的时候,applyBorder函数将会把这个值变成“10px solid red”这样的字符串。apply函数你可用可不用,但是要记得在这个函数里面必须返回一个值,否则就什么效果都没有。
The updateBorder function is called after the applyBorder function has had a chance to transform the value, and is usually used to modify the DOM, send AJAX requests or perform any other kind of processing. In our case we're just getting the view's element and updating the border style using setStyle. This means that every time setBorder is called our DOM will immediately be updated to reflect the new style.
updateBorder函数会在applyBorder已经转换了传入值之后被调用,通常被用来调整DOM,发送AJAX请求或者做些其他事情。这个例子中我们仅仅得到视图的element元素并使用setStyle方法更新其border样式,也就是说每次setBorder方法被调用后,DOM都会立即被更新成最新样式。
Here's an example of the new view in action. Click the Code Editor button to see the source - basically we just create an instance of the new view and dock a spinner field at the top, allowing us to change the border width by tapping the spinner buttons. We hook into the Spinner's spin event and call our view's new setBorder function from there:
下面就是新视图的示例代码,查看源码可知,我们创建了一个新视图的实例并且在试图内部放置一个紧贴顶部的spiiner,为实现点击spinner上的增减按钮可以改变border宽度,我们侦听spinner的spin事件,在里面调用视图setBorder方法。
//as before
//这里跟前面相比没有变化
Ext.define('MyApp.view.MyView', {
extend: 'Ext.Panel',
config: {
border: 0
},
applyBorder: function(value) {
return value + "px solid red";
},
updateBorder: function(newValue, oldValue) {
this.element.setStyle('border', newValue);
}
});
//create an instance of MyView with a spinner field that updates the border config
//创建MyView的实例,并且在其配置中使用一个spinnerfield来改变border参数
var view = Ext.create('MyApp.view.MyView', {
border: 5,
fullscreen: true,
styleHtmlContent: true,
html: 'Tap the spinner to change the border config option',
items: {
xtype: 'spinnerfield',
label: 'Border size',
docked: 'top',
value: 5,
minValue: 0,
maxValue: 100,
incrementValue: 1,
listeners: {
spin: function(spinner, value) {
view.setBorder(value);
}
}
}
});
Usage in MVC
视图在MVC中的用法
We recommend that most Sencha Touch applications should follow the MVC conventions so that your code remains well organized and reusable. As the "V" in MVC, views also fit into this structure. The conventions around views in MVC are very simple and follow directly from the naming convention we used above.
我们强烈建议绝大多数ST应用程序都应该遵循MVC的模式开发这样你的代码将会组织良好并且利于重用。作为MVC中的V,视图非常适用于这种架构。MVC中关于视图的规则非常简单并且跟我们上面用到的命名规则毫不冲突。
Our MyApp.view.MyView class should be defined inside a file called app/view/MyView.js - this allows the Application to find and load it automatically. If you're not already familiar with the file structure for MVC-based Sencha Touch apps, it's pretty simple - an app is just an html file, an app.js and a collection of models, views and controllers inside the app/model, app/view and app/controller directories:
我们的MyApp.view.MyView类应该在app/view/MyView.js文件中进行定义,这样应用程序就能自动找到和加载它。如果你对“MVC式ST应用程序”的文件架构还不熟悉,你可以这么理解:一个应用程序就是一个html、一个app.js、再加上分处于app/model、app/view、app/controller目录下的一堆model、view、controller文件的集合:
index.html
app.js
app/
controller/
model/
view/
MyView.js
You can create as many views as you want and organize them inside your app/view directory. By specifying your application's views inside your app.js they'll be loaded automatically:
你可以按照你的需要创建很多视图文件放在app/view目录里。通过在app.js中指定application的views参数,他们就能被自动加载了。
//contents of app.js
//app.js内容
Ext.application({
name: 'MyApp',
views: ['MyView'],
launch: function() {
Ext.create('MyApp.view.MyView');
}
});
By following the simple view naming conventions we can easily load and create instances of our view classes inside our application. The example above does exactly that - loads the MyView class and creates an instance of it in the application launch function. To find out more about writing MVC apps in Sencha Touch please see the intro to apps guide.
遵循这种简单的视图命名规则,我们可以在应用程序中很轻易的加载视图类并创建其实例。上述的例子就是这样做的:载入了MyView类然后在application的launch函数里创建了该类的实例。想要了解更多关于MVC式ST应用程序的编写方法,请跳转至《应用程序开发指南》页面(译者注:也就是此翻译系列的第一篇:Intro to Applications with Sencha Touch 2)。