EXT
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
目录
前言 (2)
EXTJS的缺点以及优点 (3)
EXTJS面向对象 (3)
EXTJS组件化编程 (5)
EXTJS组件之间的通信 (16)
EXTJS中的Singleton (19)
EXTJS扩展组件例子 (20)
EXTJS JavaScript的调试 .............................................................................. 错误!未定义书签。
EXTJS XTemplate .. (29)
EXTJS DataView的使用 (31)
EXTJS 优化服务器端 (41)
EXTJS与后台的通信(Java) (41)
EXTJS 在AIR中继续施展 (53)
前言
本人学习编程一年,实际开发一年。
对编程的理解还仅处于很肤浅的认识上,本文本没有打算公开出来。
但是,我一直认为与人分享劳动成果是一件非常开心的事情,不仅能让更多的新手分享到我学习的快乐,而且从中我还能学到更多的知识。
由于本文最初是一个个人的总结,后来觉得还是公开出来的好,即使自己有错,也心甘情愿的挨板砖。
落后就要挨打,挨打也得站直了。
由于我确实很菜,所以文中必然有一定的理解性错误,如果本文有幸被您看到,请切不可认定这就是问题的答案。
希望能够收到更多的板砖,这样我才能从自我的误区中走出来,如果您是新手,最好不要把这篇文章此作为技术手册,误人子弟的罪名我担当不起。
因为很多个人的误解不经过无数的板砖是无法自我发现的。
这个文章的语言比较晦涩,我也尽量的想把一些问题阐述的更清晰一些,但是无奈我现在才发现语文的重要性……
写了这么多东西,希望有人能给我的文中错误指出,中国的开源离不开您的指点,这样
我才能更好的去完善它,也让我更有信心的去开源。
我的邮箱andy_ghg@。
希望能够得到高手的指点。
也希望结交到更多的朋友。
EXTJS的缺点以及优点
EXTJS是有缺点的,缺点在于,由于过度的封装,导致我们很难对其样式有较大的改动,即使有所改动也很难达到预期的目标。
EXTJS的优点也是显而易见的,他有着丰富的控件库,有着丰富的工具包,有着优美的语法。
EXTJS很容易让一个有Swing或者Winform编程经验的人上手。
有很多人甚至根本不用知道XMLHttpRequest和ActiveXObject就可以轻易的编写AJAX程序。
但是EXTJS 是基于AJAX技术的,如果你不对AJAX有一个全面的了解,你可能会走不少的弯路,最终可能是知其然而不知其所以然。
由于EXTJS的过度封装,导致其组件的灵活性有所下降,在改变外观样式的时候,也经常出现弄巧成拙的事情。
还有最重要的一点就是EXTJS体积,EXTJS的CSS样式表文件加上主要的库文件大约能达到800KB,这还不包括他的一些图片文件。
所以这方面还是EXTJS应该改进的地方。
EXTJS面向对象
关于JavaScript的变量、对象以及一些基础知识,大家可以在《AJAX入门经典》(英文名Beginning AJAX)这本书中的第二章看到简单的描述(更详细的就直接去买本JS相关的书籍吧)。
这里不再阐述。
在EXTJS中,我们该如何管理好我们的对象?对象的管理,在以往的EXTJS开发中,最初的写法可能是面向过程的,如下:
那么在这里,位于Panel外面的对象就不会因为panel的销毁而被销毁,它可以一直存在于内存当中。
在开发中如果有多个这样的对象,那么管理起来是一个很耗费时间精力的。
这主要体现在项目后期,发布阶段,项目发布时,JS代码不应再像开发时那样分散在各个JS文件当中,而是代码的整合,全部归并到一个JS文件并扰码压缩,减少JS代码的体积提升JS代码的加载速度(最明显的可以看到ext-all.js的体积是621KB,而ext-all-debug.js
则达到了2.35MB),这样问题就很严重了,如果我在某个JS文件中定义了一个变量:
那么,这个变量(对象)就不能重名,整个项目中就不能再出现一个与其同名的变量,因此,变量名的维护也是一个浩大的工程,当然,也可以用绝对不重复的变量名,比如加长型的,或者是有标识的,但是这并不能解决根本性的问题。
那么,如何管理好这些模块内部使用的变量?
那么这就是对象化所解决的问题,首先看下面的代码:
var Dog = function(){
this.say = function(){
alert("汪汪");
};
}
var smallDog = new Dog();
smallDog.say();
当say这个函数作为Dog这个对象的一个属性存在于Dog对象内部,那么这个方法就不会与外部的方法命名重复。
这是作用域所起到的作用。
按照这个思想,我们稍微改造一下上面的代码:
Ext.onReady(function(){
var panel = new Ext.FormPanel({
items : [new Ext.form.TextField()]
})
})
将textfield放入到对象的构造方法内,那么就不会造成变量名冲突等事故的发生。
但是这只是一个简单的Panel,如果我们所需要的是一个比较大的模块怎么办?总不能在每次使用的时候都写一大堆这样的代码吧?而且我们在这里仅仅是将一个输入框放在了表单内,如果我们要实现一个组件又该如何去写呢?
那么我们现在假设有一个需求,在登录中显示类似下面的验证码:
那么,这个东西就不是简单的在items里new几个textfield就能解决问题的。
首先,我们来拆分一下这个登录窗体:
那么在验证码这块,我们需要一个Column布局的Panel(最好是Container),在这个Panel中,我们还需要两个Panel,一个是左边的验证码输入,一个是右边的验证码显示。
如果按照上面的那种对象化去写,必然会造成代码的混乱和难以理解。
会对后期的维护造成不必要的麻烦。
那么,我们该如何去完成这个组件的编写呢?这就是组件化所要完成的任务。
EXTJS组件化编程
通过查看EXTJS的源码(最简单的是Ext.Window组件),会看到ExtJS最常用的一种对象创建方式:
它是通过继承来实现了对象的封装,ExtJS最底层的那个基类是Observable,它提供了最基础的事件机制,通过层层的封装与扩展,它的派生类中是越来越庞大,所提供的功能也越来越多,就比如Window这个组件,它继承自Panel并添加了默认的resizabel,closable,draggable 等属性(Panel也可以加入类似的函数,但是需要编码实现)。
而Panel又继承自Container,并加入了渲染Title和tbar等功能。
那么,我们自己的对象也可以完全模仿这种语法进行创建如:
上面的程序会输出World,而且在看到上面的语法之后,会想到什么?
对,是Java等后台语言最常见的vo对象写法,看init函数,它就是一个构造函数,而下面则是get和set函数,不同的在哪里?那么就是prop这个属性其实在外面是可以随意调用的,无需使用get和set函数也能对其值进行修改(JavaScript就是这么“弱”……),而且init构造函数不能被重载。
题外话:EXT的extend函数的本质是封装的Prototype原型继承,而JavaScript的delete 操作符是不可以操作Prototype原型方法函数的。
所以在此处通过delete操作符是无法正常删除对象属性的从而达到提高效率的预期目标。
但是我们也有办法对其进行私有化处理!我们还有闭包。
(研究不深,在此不发表言论)。
在这里不是要完全模仿Java等后台语言,而是就这个现象进行扩展,扩展成我们自己的对象,那么基于这个事例,我们应当可以很简单的就开发出属于我们自己的组件。
例如,我们要创建一个最基本最常用的登录窗口,我们在这里将其称之为一个模块,这个模块虽然小了点,但是他具备所有模块都应当具备的特征,代码如下:
space("Gh.LoginModule");
/**
*@author葛昊龑
*@class Gh.LoginModule
*@extends Ext.Window
*/
Gh.LoginModule = Ext.extend(Ext.Window, {
title : "Login System",
draggable : false,
closable : false,
resizable : false,
mode : true,
layout : "fit",
autoHeight : true,
width : 270,
/**
*@description重写父类的初始化函数
*/
initComponent : function() {
// 使用superclass来获取父类的初始化函数,类似Java中的super
Gh.LoginModule.superclass.initComponent.call(this, arguments);
ernameField = new Ext.form.TextField({
fieldLabel : "Your Account"
});
this.passwordField = new Ext.form.TextField({
fieldLabel : "Your Password"
});
this.loginForm = new Ext.FormPanel({
frame : true,
autoHeight : true,
defaults : {
anchor : "95%"
},
items : [ernameField, this.passwordField]
});
this.add(this.loginForm);
this.addButton("Submit",this.doLoginHandler,this);
this.addButton("Regist",this.doRegHandler,this);
},
doLoginHandler : function(){
//TODO Somthing
},
doRegHandler : function(){
//TODO Somthing
}
});
Ext.onReady(function(){
var loginWin = new Gh.LoginModule();
loginWin.show();
})
效果图
那么,这样,我们实际上就利用继承来完成了我们的第一个组件--登录组件。
EXTJS对象化的特征是什么?
1.封装---变量的作用域被固定在对象内部,内部的对象将会随着对象的销毁而被销毁(虽然垃圾回收不会主动来回收,但是这一步很重要)。
封装了一些内部操作,如上面的doLoginHandler和doRegHandler,总所周知,在JavaScript中Function是作为一个特殊对象存在的,因此,这两个函数也都只存在于这个对象内部。
而不会影响到外部的变量。
2.继承---继承了Window,拥有了Window所拥有的一切属性,并为Window添加了一些东西,例如一个表单
3.多态---我可以定义其默认的closable为false,让其无法被关闭,为其添加了标题,让其拥有了默认的标题,我甚至可以让他变成一个不可以拖拽、不可以改变大小、不可以被关闭的系统登录窗口(姑且就这么理解成多态吧)。
其实做到这一步应该已经可以了,这就是第一个使用组件化方式编写的对象,但是看看这个对象哪里还有不足之处?
那就是一个字:乱。
因为这个对象也仅仅是将原先定义在外部的变量统一规划到了一个对象下,只是做到了变量统一化,但是还没有达到代码逻辑与视图的分离,这里仅仅是一个登录组件,如果这个组件是一个相当大的组件,例如OA中的工作流系统的某个场景,它不仅仅只是一个表单,它还包括一个流程图,一个Grid(或者其他)还有一两个表单,甚至还有可能会出现一个Tree,那么如果全部按照上面这种对象化的写法去做,会又出现代码与逻辑混杂,变量声明不清晰,组件与组件之间界限模糊。
如何解决这个问题?
我们来稍微改造一下这个登录窗口的写法,我们将Form表单从InitComponent函数中抽取重来放到外面去:
space("Gh.LoginModule");
/**
*@author葛昊龑
*@class Gh.LoginModule
*@extends Ext.Window
*/
Gh.LoginModule = Ext.extend(Ext.Window, {
title : "Login",
draggable : false,
closable : false,
resizable : false,
mode : true,
layout : "fit",
autoHeight : true,
width : 270,
/**
*@description重写父类的初始化函数
*/
initComponent : function() {
// 使用superclass来获取父类的初始化函数,类似Java中的super
Gh.LoginModule.superclass.initComponent.call(this, arguments);
this.add(this.getLoginForm());//只负责布局
this.addButton("Submit", this.doLoginHandler, this);
this.addButton("Regist", this.doRegHandler, this);
},
/**
*@description初始化表单对象
*@return{@link Ext.FormPanel}
*/
getLoginForm : function() {
return this.loginForm || (function() {
var usernameField = new Ext.form.TextField({
fieldLabel : "Your Account"
});
var passwordField = new Ext.form.TextField({
fieldLabel : "Your Password"
});
this.loginForm = new Ext.FormPanel({
frame : true,
autoHeight : true,
defaults : {
anchor : "95%"
},
items : [usernameField, passwordField]
});
return this.loginForm;
}.createDelegate(this))();
},
doLoginHandler : function() {
// TODO Somthing
},
doRegHandler : function() {
// TODO Somthing
}
});
Ext.onReady(function() {
var loginWin = new Gh.LoginModule();
loginWin.show();
})
效果是一样的
我们可能会注意到getFormPanel这个函数的写法,是的,这是一个简单的Singleton,form 在第一次被初始化之后,则不会再创建一次form表单对象,直到form被销毁(不存在了)为止。
那么按照这种写法,我们来解决上一节所提出的那个需求(最后的那个登录窗口):
这个登录窗体,我们需要验证码,那么如何依照组件化编写方式编写这个组件呢?按照上面的那种写法,我们开始组织我们的代码:
space("mon.Login");
/**
* @author <a href="mailto:andy_ghg@">葛昊</a></br>
* @description 登陆组件
* @version 1.0
* @class mon.Login
* @extends Ext.Window
*/
mon.Login = Ext.extend(Ext.Window, {
closable : false,
resizable : false,
draggable : false,
modal : true,
title : "登陆",
width : 300,
autoHeight : true,
layout : "fit",
PROJECT_NAME : "/HRM/",
initComponent : function() {
mon.Login.superclass.initComponent.call(this, arguments);
this.addEvents("loginSuccess");
this.add(this.getFormPanel());
this.addButton("登陆", this.loginBtnHandler, this);
},
/**
* 获取登陆表单
* @return {@link Ext.FormPanel}
*/
getFormPanel : function() {
return this.formPanel || (function() {
this.formTop = new Ext.Panel({
layout : "form",
defaultType : "textfield",
defaults : {
anchor : "95%",
allowBlank : false,
vtype : "alphanum"
},
labelWidth : 55,
items : [{
fieldLabel : "用户名",
emptyText : "请输入您的账号名称",
name : "username"
}, {
fieldLabel : "密码",
name : "password",
inputType : "password"
}]
})
this.formPanel = new Ext.FormPanel({
frame : true,
autoHeight : true,
items : [this.formTop, this.getValicodeImage()]
});
return this.formPanel;
}.createDelegate(this))();
},
/**
* 获取验证码图片
* @return {@link HRM.External.Image}
*/
getImage : function() {
return this.valiImage || (function() {
this.valiImage = new HRM.External.Image({
height : 20,
width : 60,
imageSrc : this.PROJECT_NAME + "servlet/ValicodeServlet"
});
return this.valiImage;
}.createDelegate(this))();
},
/**
* 获取验证码输入部件
* @return {@link Ext.Panel}
*/
getValicodeImage : function() {
return this.valicode || (function() {
this.valicode = new Ext.Panel({
layout : "column",
items : [{
xtype : "panel",
columnWidth : .7,
labelWidth : 55,
layout : "form",
items : [{
xtype : "textfield",
anchor : "100%",
fieldLabel : "验证码",
name : "valicode"
}]
}, {
xtype : "panel",
columnWidth : .3,
items : this.getImage()
}]
});
return this.valicode;
}.createDelegate(this))();
},
/**
* 登陆按钮处理函数
*/
loginBtnHandler : function() {
this.getFormPanel().getForm().submit({
url : this.PROJECT_NAME + "account_login.do",
scope : this,
success : function(form, action) {
if (action.result.data == "SUCCESS") {
this.fireEvent("loginSuccess");
} else {
Ext.MessageBox.show({
title : "失败",
msg : action.result.data,
icon : Ext.MessageBox.W ARNING,
buttons : Ext.MessageBox.OK,
fn : function(){
form.findField("password").reset();
form.findField("valicode").reset();
form.findField("username").selectText();
}
});
this.getImage().setImage(this.PROJECT_NAME + "servlet/ValicodeServlet?rand="+Ext.id());
}
},
failure : function(form, action) {
}
});
},
/**
* 销毁函数
*/
onDestroy : function(){
mon.Login.superclass.onDestroy.apply(this,arguments);
if (Ext.isIE) {
CollectGarbage();
}
}
})
//Ext.onReady(function() {
// Ext.QuickTips.init();
// var w = new mon.Login();
// w.show();
//})
将Form表单定义到InitComponent函数外面去,好处就在于,组件内部我们做到的清晰的划分。
这个登录可能还不明显,如果就像上面提到的OA一样,我们在一个界面中既需要一个工作流设计器,又需要一个Tree或者Grid,再夹杂着一个FormPanel,那么这么做则效果很明显,表单是表单,Grid是Grid,在组件的内部我们清晰了划分了两者之间的界线,就不会造成两者之间起冲突。
组件化的简单定义:组件化的基础是面向对象,在面向对象编程的基础上将一个或多个小部件封装到一起,成为一个模块,让其成为一个新的组件(适合我们需求的组件),每一个模块都应当具备单独运行的能力。
大家如果仔细阅读上面的代码,可能会注意到我在里面使用了new HRM.External.Image()这个东东,这个东西就是在外部所写的一个验证码组件,我登陆的时候要用,我在其他需要现实图片的时候也要用,所以我就将其单独抽取出来放到了外面,便于重复利用。
所以,当大家适应这种写法以后,你会发现ExtJS真的很容易学。
附带那个图片的实现方法:
space("HRM.External.Image");
/**
* @author <a href="mailto:andy_ghg@">葛昊</a></br>
* @version 1.0
* @description 图片组件
* @class HRM.External.Image
* @extends Ext.BoxComponent
*/
HRM.External.Image = Ext.extend(Ext.BoxComponent, {
imageSrc : "",
initComponent : function() {
HRM.External.Image.superclass.initComponent.call(this, arguments);
this.on("render",function(){
this.setImage(this.imageSrc);
},this);
},
/**
* 获取XTemplate对象
* @return {@link Ext.XTemplate}
*/
getXTemplate : function() {
return this.xtpl || (function() {
this.xtpl = new Ext.XTemplate("<div><img id='{id}' src='{imgSrc}' height='{height}' width='{width}' border='{border}' /></div>");
return this.xtpl;
}.createDelegate(this))();
},
/**
* 获取图片属性对象
* @return {Object}
*/
getImage : function() {
return this.imageData || (function() {
this.imageData = {
id : this.getId()+"_img",
imgSrc : "",
height : this.height,
width : this.width,
border : 0
}
return this.imageData;
}.createDelegate(this))();
},
/**
* 设置图片路径
* @param {String} src 图片路径
*/
setImage : function(src) {
this.getImage().imgSrc = src;
this.getXTemplate().overwrite(this.getEl(),this.getImage());
}
});
用面向对象的思想去编写EXTJS组件其实是一件很简单的事情,我们仅需要继承自它的某个组件就完全能够做出符合我们需要的新的模块组件。
我们不仅仅可以为它添加新的自
定义事件,也可以添加新的公共函数。
在initComponent中通常只应当做一些布局代码和自定义事件(部分情况下自定义事件也不用写在initComponent函数中):
initComponent : function() {
// 使用superclass来获取父类的初始化函数,类似Java中的super
Gh.LoginModule.superclass.initComponent.call(this, arguments);
this.addEvents(
//在点击登录按钮后,如果后台返回登录成功则触发此事件
"loginSuccess",
//在点击登录按钮后,如果后台返回登录失败则触发此事件
"loginFailure"
);
this.add(this.getLoginForm());//只负责布局
this.addButton("Submit", this.doLoginHandler, this);
this.addButton("Regist", this.doRegHandler, this);
}
在日常开发中,在组件最底层的通常被称为容器,一个Panel可以是一个容器,一个Window也可以是一个容器,具体在这个容器中放些什么东西,怎么放则随你所欲。
EXTJS组件之间的通信
很多人问过如果这样写下去,组件与组件之间如何通信,我们有很多种方法来做到组件与组件之间的通信,比如对象公开函数,让函数来改变内部的变量值,比如Panel的setTitle 函数,通过函数的调用就可以改变其title的值,从而做到消息的通知,借用上面的一段代码事例:
我们可以通过set和get方法改变对象内部的属性值,那么我们也可调用函数来做诸如,store 的刷新,内部组件的显示与隐藏等。
这仅仅是外部调用内部,那么内部如何主动通知外部呢?
那么,这就是事件机制,EXTJS的事件是通过闭包来实现的,我们在这里不研究他的事件是如何创建的,我们现在只来研究如何使用自定义事件来达到我们的目的,继续看上面的登录模块的自定义事件:
this.addEvents(
//在点击登录按钮后,如果后台返回登录成功则触发此事件
"loginSuccess",
//在点击登录按钮后,如果后台返回登录失败则触发此事件
"loginFailure"
);
我们使用this.addEvents函数来为我们的组件添加自定义事件,上面也曾提过,Extjs所有的组件(包括UI组建和Util组件)绝大部分都是由Observable派生来的,而Observable则提供了最原始的事件添加与移除的方法函数:
代码较多,省略若干
那么我们就可以使用addEvents函数来为我们的组件添加自定义事件,使用fireEvent函数来触发我们的事件,并且,fireEvent函数支持给事件附加参数,例如我们点击登录按钮通过Ajax提交表单,表单返回了一个登录失败,并且给出了失败原因,我们可以在doLoginHandler 回调函数中这样模拟一个失败的触发:
那么我们可以看到,failure回调函数中的事件触发多了一个参数,即"URL lost"字符串,这就是告诉外面的观察者,我们登录失败了,我们还告诉你失败的具体原因。
那么会有人问,我们该如何定义这个观察者呢?
观察者是一种设计模式,至于详细信息,网络上也有很详尽的诠释,我们在此不做讨论。
一个观察者的定义其实很简单,我们可以调用on函数来观察某个事件的发生,例如刚才的登录,我们可以在外部这么定义观察者:
这样,我们就完成了一个观察者,观察者一般常用的会有三个参数(第四个参数为配置参数,很少用到),第一个参数为你想要观察的那个事件,第二个参数为处理这个事件的那个函数,第三个参数为,处理这个事件的作用域指向何处。
在这里,我们在监听函数仅仅是简单的弹出了一个警告,所以就没有传第三个参数给它(Scope),程序的运行效果如下:
看到弹出的窗口,如果你仔细看,你会发现代码中的Ext.Msg.alert("T","C");没有使用new关键字来创建,那么在Extjs中,它是如何做到的?
EXTJS中的Singleton
在Extjs中是有“单例”的(API中明确指定MessageBox为一个“单例”,注意我的引号),比如我们最熟悉的MessageBox类,看一下它的类的声明方式,原来他其实相当于是事先创建好了对象(Ext.Msg 就是Ext.MessageBox):
注意黄色荧光笔标记处
它return了一个对象,那么我们也可以写一个静态类,类似于它:
我们在内存中预先定义了一个变量,名字就叫Gh.StaticClass,在函数执行之后,这个变量就指向了他内部return的那个对象,在JavaScript中,不用var关键字创建的对象即为全局变量(注意),那么这个全局变量在初始化之后,就可以在整个项目中使用了。
扩展一下思维,在EXTJS中如果要使用一个带图标的对话框,我们往往需要如下编写代码:
Ext.onReady(function(){
Ext.MessageBox.show({
title : "Test",
icon : Ext.Msg.WARNING,
buttons : Ext.Msg.OK,
minWidth : 200,
modal : true
})
})
那么每次这么写,显然是一件非常繁琐的事情,我们可以写一个函数给它封装一下如下:ShowMsg = function(){
return {
show : function(msg,title,icon,buttons){
Ext.Msg.show({
title : title?title : "信息",
msg : msg?msg : "无提示内容",
icon : icon?icon : ,
buttons : buttons?buttons : Ext.Msg.OK
});
}
}
}();
这样,我们就不必每次都要写那么长长的一段代码了。
原来如此简单,套用Beginning Ajax 作者的那句话:“生活如此简单该多好!”。
EXTJS扩展组件例子
Ext.ux.TreeCombo
space("HRM.External.TreeCombo");
/**
*@author葛昊
*@class HRM.External.TreeCombo
*@extends boBox
*/
HRM.External.TreeCombo = Ext.extend(boBox,{ initComponent:function(){
boTree.superclass.initComponent.call(this,arguments) ;
this.tpl = new Ext.XTemplate(
'<tpl for=".">',
'<div style = "height:200px;"><div id = "treeCombo_'+this.getId()+'"></div></div>',
'</tpl>'
);
var proxy = this;
this.tree = new Ext.tree.TreePanel({
border:false,
autoScroll:true,
anchor:"100%",
loader:new Ext.tree.TreeLoader({
dataUrl:proxy.dataUrl
}),
root:this.treeNode,
listeners:{
"click":function(n,e){
proxy.setValue(n.id);
proxy.setRawValue(n.text);
proxy.collapse();
}
}
});
this.on("expand",function(){
this.tree.render("treeCombo_"+this.getId());
},this);
}
})
Ext.ux.GridCombo
space("HRM.External.GridCombo");
/**
*@author葛昊
*@version 1.0,2009年8月14日1:19:28
*@class HRM.External.GridCombo
*@extends boBox
*/
HRM.External.GridCombo = Ext.extend(boBox, { store : new Ext.data.SimpleStore({
fields : [],
data : [[]]
}),
hideValue : {},
initList : function() {
this.list = this.getGridPanel(true);
},
getGridPanel : function(autoCreate) {
return this.gridPanel || (autoCreate ? (function() { this.gridPanel = new Ext.grid.GridPanel({
store : this.gridStore,
cm : this.gridCm,
floating : true,
width : 300,
height : 200,
bbar : new Ext.PagingToolbar({
store : this.gridStore,
pageSize : 50,
displayInfo : true
}),
viewConfig : {
forceFit : true,
autoFill : true
},
alignTo : function(el, pos) {
this.setPagePosition(this.el.getAlignToXY(el, pos));
},
listeners : {
"rowdblclick" : this.onRowDblClick,
scope : this
}
});
return this.gridPanel;
}.createDelegate(this))() : Ext.emptyFn());
},
expand : function() {
if (!this.list.rendered) {
this.list.render(document.body);
this.list.setWidth(400);
this.innerList = this.list.body;
this.list.hide();
}
this.el.focus();
Ext.ux.GridCombo.superclass.expand.apply(this, arguments);
},
doQuery : function(q, forceAll) {
this.expand();
this.gridStore.load();
},
collapseIf : function(e) {
if (!e.within(this.wrap) && !e.within(this.list.el)) { this.collapse();
}
},
onRowDblClick : function(grid, rowIndex, e) {
this.hideValue.value = grid.getStore().getAt(rowIndex).get(this.gridHiddenField);
this.setRawValue(grid.getStore().getAt(rowIndex).get(this.grid DisplayField));
this.fireEvent("select");
try {
this.collapse();
} catch (e) {
}
},
getValue : function() {
return this.hideValue.value
}
})
Ext.ux.VideoPlayer
space("GH.External.VideoPlayer");
/**
* Flash播放器,用于播放视频。
关于Socket通信具体请查看<i>Flash源文件文档</i>。
* @author <a href="mailto:andy_ghg@">葛昊</a>
* @version 1.0
* @example
* <code>Ext.onReady(function() {
var flash = new GH.External.VideoPlayer({
width : 320,
height : 240,
renderTo : Ext.getBody()
});
Ext.get("btn").on("click",function(){
flash.initVideo("rtmp://127.0.0.1/live","testTT",true);
});
});</code>
* @class <b>GH.External.VideoPlayer</b>
* @extends {@link Ext.FlashComponent}
*/
GH.External.VideoPlayer = Ext.extend(Ext.FlashComponent, {
/**
* Flash文件的路径
* @type String
*/
url : "/MyQQ/ext-3.1.0/resources/QQPlayer.swf",
height : 240,
width : 320,
initComponent : function() {
GH.External.VideoPlayer.superclass.initComponent.call(this, arguments);
this.addEvents(
/**
* 是否连接成功
*/
"connected"
);
},
/**
* 获取Flash对象
* @private
* @return {Object}
*/
getFlash : function() {
if (Ext.isIE) {
return window[this.id];
} else {
console.log(document[this.id]);
return document[this.id];
}
},
/**
* 初始化Video,告诉Video将要播放的视频信息。
* @param {String} url FMS流媒体服务器提供的流媒体地址
* @param {String} name 发布方的流名称
* @param {Boolean} localMedia 是否发送本地视频
*/
initVideo : function(url,name,localMedia,showLocal){
try{
this.getFlash().onInit(url,name,localMedia,showLocal);
console.log("初始化完毕");
this.fireEvent("connected");
}catch(e){
ShowMsg("视频播放器未加载完毕,请稍后再试。
");
}
}
});
播放器的Swf源码:
/**
* @auther 葛昊
* @version 1.0
* @desc Flash播放器。
在FMS下测试正常
*/
package doc {
import StatusEvent;
import flash.media.Video;
import flash.text.TextField;
import Connection;
import Stream;
import .XMLSocket;
import flash.display.Sprite;
import flash.media.Camera;
import flash.media.Microphone;
import flash.external.ExternalInterface;
import flash.system.Security;
public class QQPlayer extends Sprite {
private var socket:XMLSocket;//Socket通信
private var conn:NetConnection;//网路链接
private var inStream:NetStream;//接收流
private var outStream:NetStream;//输出流
private var videoPlayer:Video;//播放器
private var videoMsg:TextField;//消息显示
private var netURL:String;//视频流地址
private var streamName:String;//视频流名称
private var camera:Camera;
private var microPhone:Microphone;
private var openLocalMedia:Boolean = false;//是否打开本地视频
private var showLocalMedia:Boolean = false;//当前播放器的类型,是播放本地还是远程
// Initialization:
public function QQPlayer() {
Security.allowDomain("http://localhost:8080/MyQQ/");
trace("进入构造方法");
//添加播放器
videoPlayer = new Video();
videoPlayer.height = stage.stageHeight;
videoPlayer.width = stage.stageWidth;
this.addChild(videoPlayer);
//添加媒体流信息文本框
videoMsg = new TextField();
videoMsg.selectable = false;
videoMsg.text = "状态:无视频流";
videoMsg.x = 20;
videoMsg.y = 20;
this.addChild(videoMsg);
//初始化Socket,用于消息传递
socket = new XMLSocket();
//初始化网络连接
conn = new NetConnection();
conn.client = this;
ExternalInterface.addCallback("onInit",onInit);//供JavaScript调用
//onInit("rtmp://127.0.0.1/live","testTT",true);
}
/**
* 初始化链接,由JavaScript来决定具体要链接到那个流上面去
* @param url 链接地址
* @param sname 流名称
* @param openLocalMediao 是否打开本地视频
*/
public function onInit(url:String,sname:String,openLocal:Boolean=false,showLocal:Boolean=true):void {
this.showLocalMedia = showLocal;
this.openLocalMedia = openLocal;
URL = url;
this.streamName = sname;
this.conn.connect(url);
this.conn.addEventListener(_STATUS,onConnectedHandler);
}
/**
* Connection链接监听事件,监听是否连接成,并初始化网络流
*/
protected function onConnectedHandler(e:NetStatusEvent):void
{
if(.code == "NetConnection.Connect.Success"){
this.camera = Camera.getCamera();
this.camera.setMode(200,154,20);
this.camera.setQuality(0,100);
if(this.showLocalMedia){
videoMsg.text = "状态:本地视频流";
this.videoPlayer.attachCamera(this.camera);
}else{
videoMsg.text = "状态:网络视频流";
this.inStream = new NetStream(conn);
this.inStream.client = this;//默认为this,如有需要,可编写自定义客户端
this.inStream.addEventListener(_STATUS,onInStreamHandler);
this.inStream.play(this.streamName);//开始播放流
this.videoPlayer.smoothing=true;//平滑处理
this.videoPlayer.attachNetStream(inStream);//将inStream作为播放器的数据来源
}
if(this.openLocalMedia){
this.microPhone = Microphone.getMicrophone();
this.outStream = new NetStream(this.conn);
this.outStream.addEventListener(_STATUS,onOutStreamHandler);
this.outStream.attachAudio(this.microPhone);
this.outStream.attachCamera(this.camera);
this.outStream.publish("testTT","live");
}
}else{
trace(.code);
}
}
/**
* NetStream的监听事件,如有必要可以再内部添加一些函数
*/
protected function onInStreamHandler(e:NetStatusEvent):void
{
//TODO Somthing
}
protected function onOutStreamHandler(e:NetStatusEvent):void
{
//TODO Somthing
}
/**
* 供FMS回调使用
*/
public function onMetaData(info:Object):void
{
}
/**
* 供FMS视频播放结束后调用
*/
public function onPlayStatus(info:Object):void
{
}
public function onBWDone():void
{
}
}
}
有了上面的代码,我们完全可以做一个能即时聊天,还能视频的webQQ有兴趣的可以试一下,下面是我写的一个半拉子工程的效果图:
顺便说一下,做RIA程序,AS3是有学习的必要的。
EXTJS XTemplate
如果我们有比较特殊的需求,比如要在一个Panel中显示一件商品,我需要显示其详细信息,比如说,图片,价格等,那么我们又厌倦了Extjs千篇一律的面孔,我们可以使用Ext.XTemplate这个类来做到我们想要的效果,就例如这个文档的封面,那是用XTemplate 做的,里面不仅有图片还有信息。
XTemplate的作用就在于修改DOM节点,它提供了一系列方法供开发者来改变原有组件。
关于上面的例子请看下面的代码:
space("MyFace");
MyFace = Ext.extend(Ext.Window, {
layout : "fit",
height : 400,
width : 400,
title : "流水无心囍",
initComponent : function() {
MyFace.superclass.initComponent.call(this,arguments);
this.add(this.getPanel());
},
getPanel : function() {
return this.panel || (function() {。