`

教你创建Ext UI控件(转载)

阅读更多

使用ExtJs创建新的UI控件

此文档介绍了怎么在ExtJS 2.x的库中,把现有类的能力扩展为新的用户界面控件。如果阁下想就此文进行讨论,请到论坛的帖子

组合或扩展

当创建一个新类,往往要作出这么的一个选择:要么拥有某个工具类的实例来扮演首要的角色,要么扩展那个类。

使用ExtJs过程中,推荐从最靠近的基类开始扩展,实现所需的功能即可。这是因为Ext提供的自动生存周期引入了自动渲染的机制、自动大小调整和承担接受来自布局管理器的UI组件布局调控,还有在容器(Container)中自动销毁的功能。

组织一个新类,它就是ExtJs的类,实现起来是很方便的,这就会导致了Container→Component层次的形成,相比较,新类拥有一个ExtJs类的话,必须从外围对其渲染和组织。

The Template method Pattern模板方法模式

Ext Js的Component的继承层次采用Template Method pattern(模板方法模式)来委派给子类,交由子类来负责其特定的行为。

其意义在于,继承链中的每个子类得以在,组件生存周期的某个阶段内,“提交(contribute)”额外的逻辑功能。这样每一个类拥有属于其自身的行为;其他类加入自己的行为时也不会相互造成影响。

其中一个例子是render(渲染)函数。render函数不能够被覆盖,应该是一层一层地在各子类的实现中加入onRender方法,那么render函数就会在执行的时候把各onRender方法访问调用。每一个onRender方法必须调用其父类的onRender方法继而再“埋头处理(contribute)”自己(子类)的逻辑部分。

下面的图例演示了模板方法onRender的机能过程。

render是由容器的布局管理器(Container’s layout manager)负责调用。该方法的实现不能被“冲掉(overridden)”,它是由Ext基类提供的。this.onRender表示当前子类所写的实现(如有提供的话)。它会访问父类的版本、父类的版本又会调用父、父类的版本……最终,每个类完成了其功能,render函数的返回值对生存周期进行控制。

 

在ExtJS组件(Component)生存周期中,提供了若干有意义的模板方法以实现类特定的逻辑功能。

强调: 当编写子类时,模板方法应在实例化的过程中调用,属于生存周期内的一部分的动作,而不应事件的一部分。事件有可能会由handler挂起,或中止。

以下是Component子类都可享有的模板方法:

  • onRender
允许渲染期间加入特定的行为。父类的onRender被调用后,可以确定组件的Element元素。可以在此阶段中执行剩余DOM任务以完成结构上的控制(HTML结构)。
  • afterRender
允许渲染完成后加入特定的行为。此阶段的组件元素会根据配置的要求(configuartion)设置样式,会引入已配置的CSS样式名称所指定的样式名称,并会配置可见性(visibility)和配置可激活(enable)情况。
  • onShow
允许在显示组件的同时加入特定的行为。父类的onShow执行过后,Componenet将会显示。
  • onHide
允许在隐藏组件的同时加入特定的行为。父类的onHide执行过后,Componenet将会隐藏。
  • onDisable
允许在禁用组件的同时加入特定的行为。父类的onDisable执行过后,Componenet将会禁用。
  • onEnable
允许在激活组件的同时加入特定的行为。父类的onEnable执行过后,Componenet将会激活启用。
  • onDestroy
允许在销毁组件的同时加入特定的行为。父类的onDestroy执行过后,Componenet将会被销毁。

Ext组件类的各层次中的均有其自身的模板方法,我们可以打开来看看,这些都是根据自身不同的需求而作出的设计。

提示: 当调用父类的模板方法时,最简洁的方法就是使用Function.apply,保证所有的参数都可以接受得到,传送给那个模板方法:

Ext.ux.Subclass.superclass.onRender.apply(this, arguments);

要扩展哪个类

选择适合的类来扩展不但要考虑基类提供哪些功能,而且对性能方面也要着重考虑。无论有多少个UI控件被渲染或调控,Ext.Panel常常就是被衍生(extend)的对象。

Panel类拥有许多的能力:

  • Border(躯干)
  • Header(头部)
  • Header工具条
  • Footer(底部)
  • Footer按钮
  • Top toolbar(顶部工具条)
  • Bottom toolbar(底部工具条)
  • 承托和管理子组件

如果这些派不上用场,那使用Panel便是资源浪费。

Component(组件类)

如果要求的UI控件不需要其他的细节的控件,也就是,仅仅是封装某部分的HTML元素的话,那么可取的扩展对象就是Ext.BoxComponentExt.Component。如果再缩窄一步,我不需要听凭父容器提供的大小调控功能,那么使用Ext.Component就可以了。

强调: Component类并不会内省而得知哪一种元素作为holder。因此为了创建所需的元素(Element),应设定autoEl的配置项。

例如,要把一张图片封装为Component,我们于是乎这样定义:

Ext.ux.Image = Ext.extend(Ext.Component, {
    autoEl: {
        tag: 'img',
        src: Ext.BLANK_IMAGE_URL,
        cls: 'tng-managed-image'
    },
 
//  Add our custom processing to the onRender phase.
//  We add a ‘load’ listener to our element.
    onRender: function() {
        Ext.ux.Image.superclass.onRender.apply(this, arguments);
        this.el.on('load', this.onLoad, this);
    },
 
    onLoad: function() {
        this.fireEvent('load', this);
    },
 
    setSrc: function(src) {
        this.el.dom.src = src;
    }
});

这是一个可封装图片的Ext Component类,可参与非箱子方寸模型(non box-sizing)的布局。

BoxComponent

如果要求的UI控件不需要其他的细节的控件,也就是,仅仅是封装某部分的HTML元素的话,还要听凭布局管理器提供的大小尺寸、布局的调控,那么这个的扩展对象就是Ext.BoxComponent。

例如,假设一个Logger类打算是简单地显示log信息,就必须嵌入某种布局的风格,例如插入到一个layout:’fit’窗体,可以这样定义:

Ext.ux.Logger = Ext.extend(Ext.BoxComponent, {
    tpl: new Ext.Template("<li class='x-log-entry x-log-{0:lowercase}-entry'>",
        "<div class='x-log-level'>",
            "{0:capitalize}",
        "</div>",
        "<span class='x-log-time'>",
            "{2:date('H:i:s.u')}",
        "</span>",
        "<span class='x-log-message'>",
            "{1}",
        "</span>",
    "</li>"),
 
    autoEl: {
        tag: 'ul',
        cls: 'x-logger'
    },
 
    onRender: function() {
        Ext.ux.Logger.superclass.onRender.apply(this, arguments);
        this.contextMenu = new Ext.menu.Menu({
            items: [new Ext.menu.CheckItem({
                id: 'debug',
                text: 'Debug',
                checkHandler: Ext.ux.Logger.prototype.onMenuCheck,
                scope: this
            }), new Ext.menu.CheckItem({
                id: 'info',
                text: 'Info',
                checkHandler: Ext.ux.Logger.prototype.onMenuCheck,
                scope: this
            }), new Ext.menu.CheckItem({
                id: 'warning',
                text: 'Warning',
                checkHandler: Ext.ux.Logger.prototype.onMenuCheck,
                scope: this
            }), new Ext.menu.CheckItem({
                id: 'error',
                text: 'Error',
                checkHandler: Ext.ux.Logger.prototype.onMenuCheck,
                scope: this
            })]
        });
        this.el.on('contextmenu', this.onContextMenu, this, {stopEvent: true});
    },
 
    onContextMenu: function(e) {
        this.contextMenu.logger = this;
        this.contextMenu.showAt(e.getXY());
    },
 
    onMenuCheck: function(checkItem, state) {
        var logger = checkItem.parentMenu.logger;
        var cls = 'x-log-show-' + checkItem.id;
        if (state) {
            logger.el.addClass(cls);
        } else {
            logger.el.removeClass(cls);
        }
    },
 
    debug: function(msg) {
        this.tpl.insertFirst(this.el, ['debug', msg, new Date()]);
        this.el.scrollTo("top", 0, true);
    },
 
    info: function(msg) {
        this.tpl.insertFirst(this.el, ['info', msg, new Date()]);
        this.el.scrollTo("top", 0, true);
    },
 
    warning: function(msg) {
        this.tpl.insertFirst(this.el, ['warning', msg, new Date()]);
        this.el.scrollTo("top", 0, true);
    },
 
    error: function(msg) {
        this.tpl.insertFirst(this.el, ['error', msg, new Date()]);
        this.el.scrollTo("top", 0, true);
    }
});

接着是CSS:

.x-logger {
    overflow: auto;
}
.x-log-entry .x-log-level {
    float: left;
    width: 4em;
    text-align: center;
    margin-right: 3px;
}
.x-log-entry .x-log-time {
    margin-right: 3px;
}
.x-log-entry .x-log-message {
    margin-right: 3px;
}
.x-log-debug-entry, .x-log-info-entry, .x-log-warning-entry, .x-log-error-entry {
    display: none;
}
 
.x-log-show-debug .x-log-debug-entry { display: block }
.x-log-show-info .x-log-info-entry { display: block }
.x-log-show-warning .x-log-warning-entry { display: block }
.x-log-show-error .x-log-error-entry { display: block }
 
.x-log-debug-entry .x-log-level { background-color: #46c }
.x-log-info-entry .x-log-level  { background-color: green }
.x-log-warning-entry .x-log-level  { background-color: yellow }
.x-log-error-entry .x-log-level  { background-color: red }

我们吧log的信息的HTML列表均放置在一个布局中。我们在onRender的阶段加入处理,使得右键菜单可以根据CSS样式类的名称操控logged条目的可见性。位于该层次的对象还提供了特别的模板方法:

  • onResize
此时此刻,BoxComponent的大小已经发生变化,此时可执行剩余的任务。
  • onPosition
此时此刻,BoxComponent的定位已经发生变化,此时可执行剩余的任务。

Container(容器类)

如果要求的UI控件将用于承载(Contain)其他UI元素在其身上,但并不需要前文提及到的Ext.Panel那么多的功能,为避免臃肿,应采用Ext.Container容器类来继承。同样地,autoEl指定元素的配置项亦必不可少,将用于容器在某个元素之上进行渲染。同样,在视觉控制方面,滚动条是否显示方面(即overflow属性),用户都可以使用Style配置项,或容器元素的class属性的两种方式进行CSS样式制定。

注意: 对于Container层次,不要忘记哪种布局类是被用于渲染和调控子组件的。

示例中的类封装了条件命令的查询,允许用户对Store基于测试字段的数据筛选。除了功能上的封装外,还把查询任务作统一布局,封装在一个可控类中,可方便从容器身上自动添加或移除查询的条目,灵活性更高:

Ext.ux.FilterCondition = Ext.extend(Ext.Container, {
    layout: 'table',
 
    layoutConfig: {
        columns: 7
    },
 
    autoEl: {
        cls: 'x-filter-condition'
    },
 
    Field: Ext.data.Record.create(['name', 'type']),
 
    initComponent: function() {
        this.fields = this.store.reader.recordType.prototype.fields;
        this.fieldStore = new Ext.data.Store();
 
//      Create a Store containing the field names and types
//      in the passed Store.
        this.fields.each(function(f) {
            this.fieldStore.add(new this.Field(f))
        }, this);
 
//      Create a Combo which allows selection of a field
        this.fieldCombo = new Ext.form.ComboBox({
            triggerAction: 'all',
            store: this.fieldStore,
            valueField: 'name',
            displayField: 'name',
            editable: false,
            forceSelection: true,
            mode: 'local',
            listeners: {
                select: this.onFieldSelect,
                scope: this
            }
        });
 
//      Create a Combo which allows selection of a test
        this.testCombo = new Ext.form.ComboBox({
            triggerAction: 'all',
            store: ['<', '<=', '=', '!=', '>=', '>']
        });
 
//      Inputs for each type of field. Hidden and shown as necessary
        this.booleanInput = new Ext.form.Checkbox({
            hideParent: true,
            hidden: true
        });
        this.intInput = new Ext.form.NumberField({
            allowDecimals: false,
            hideParent: true,
            hidden: true
        });
        this.floatInput = new Ext.form.NumberField({
            hideParent: true,
            hidden: true
        });
        this.textInput = new Ext.form.TextField({
            hideParent: true,
            hidden: true
        });
        this.dateInput = new Ext.form.DateField({
            hideParent: true,
            hidden: true
        });
 
        this.items = [ this.fieldCombo, this.testCombo, this.booleanInput, this.intInput, this.floatInput, this.textInput, this.dateInput];
        Ext.ux.FilterCondition.superclass.initComponent.apply(this, arguments);
    },
 
    onFieldSelect: function(combo, rec, index) {
        this.booleanInput.hide();
        this.intInput.hide();
        this.floatInput.hide();
        this.textInput.hide();
        this.dateInput.hide();
        var t = rec.get('type');
        if (t == 'boolean') {
            this.booleanInput.show();
            this.valueInput = this.booleanInput;
        } else if (t == 'int') {
            this.intInput.show();
            this.valueInput = this.intInput;
        } else if (t == 'float') {
            this.floatInput.show();
            this.valueInput = this.floatInput;
        } else if (t == 'date') {
            this.dateInput.show();
            this.valueInput = this.dateInput;
        } else {
            this.textInput.show();
            this.valueInput = this.textInput;
        }
    },
 
    getValue: function() {
        return {
            field: this.fieldCombo.getValue(),
            test: this.testCombo.getValue(),
            value: this.valueInput.getValue()
        };
    }
});

此类管理了其包含的输入字段,可以精确的布局-大小调整,外补丁等等——都是通过CSS样式分配到元素身上这样来起作用的。

位于该层次的对象还提供了特别的模板方法:

  • onBeforeAdd
当添加新的子组件的时候,就会调用该方法。这时会有新组件作为参数传入,或者可修改它,或者以特别的方式准备好Container。返回false表示终止添加的操作。

Panel

如果所需的UI控件要求头部、底部、或工具条之类的元素,那么Ext.Panel就是一个很不错的类给予继承了。

注意: Panel是容器的一种,不要忘记哪种布局类是被用于渲染和调控子组件的。

通常Ext.Panel所实现的类会有很高的程序结合性,一般用于与其他UI控件协调使用(通常Containers,或表单字段),并对其有特定配置的布局风格。另外,要对在其内的组件提供操作的命令,可以从tbar(顶部工具栏)bbar(底部工具栏)的两方面设置加以控制。

Field

如果所需的UI控件要求为用户交互,可以把程序的数据显示给用户,或修改进而发生给服务器的功能,那么要被扩展的类应该是Ext.form.TextField,或Ext.Form.NumberField。另外,如果要求轮换按钮(Trigger button),以备键盘按键的轮换,那就是Ext.form.TriggerField

位于该层次的对象还提供了特别的模板方法:

  • onFocus:input输入框得到焦点后即会触发该方法的执行。
  • onBlur:input输入框失去焦点后即会触发该方法的执行。

什么时候不需要子类

有些时候,滥用子类无异于“杀鸡用牛刀”。在一些特定应用场合,某个现有的类它的方法被添加、被重写,是由这个类的构造器实例化过程中依靠参数传入的。

  • 大小: 34 KB
分享到:
评论

相关推荐

    Ext官方中文教程(可打包下载)

    教你创建Ext UI控件 事件的处理 Ext中的继承 Ext的类设计 Ajax通讯 JSON处理方法 函数的原型扩展 组件的使用: Tab标签页 Ext 1.x中的布局 Grid组件初学 Grid的数据分页 Ext菜单器件 表单组件初学 表单组件初学...

    Ext做的时间段选择控件

    自己第一次用Ext做的时间段选择控件,是参考公司了另一同事的控件修改的希望对大家有用。

    Extjs UI控件 for Asp.net3.5

    Ext控件 Asp.net vs2008测试通过

    Ext学习的PPT及简单的介绍Ext的控件

    Ext是一个Ajax框架,用于在客户端创建丰富多彩的web应用程序界面,是在Yahoo! UI的基础上发展而来的。官方网址:http://www.extjs.com/ Ext的几个特点  1、使用标准的W3C技术;  2、庞大的组件模型及控件库;  3...

    jsr2控件——快速组建WEB前台应用的UI控件包

    一款挺不错的UI控件,值得收藏,由国人编写,界面类似于EXT,但要比EXT小的多

    EXT2.0中文教程

    让你知道ext表格控件的厉害。 2.1. 功能丰富,无人能出其右 2.2. 让我们搞一个grid出来耍耍吧。 2.3. 上边那个是1.x的,2.0稍微有些不同哦 2.4. 按顺序,咱们先要把常见功能讲到 2.4.1. 自主决定每列的宽度 2.4.2. ...

    国内首个基于Ext开发开源企业级框架Efsform

    Efs 是企业快速开发的UI 层。这个UI 层封装extjs 框架,Ext 提供了好的 页面布局方式、功能强大的组件、优质的页面风格,但是如果直接使用Ext又是 ...当然你也可以使用new Ext 的控件渲染到 页面,两者可以混合使用。

    EXT教程EXT用大量的实例演示Ext实例

    让你知道ext表格控件的厉害。 2.1. 功能丰富,无人能出其右 2.2. 让我们搞一个grid出来耍耍吧。 2.3. 上边那个是1.x的,2.0稍微有些不同哦 2.4. 按顺序,咱们先要把常见功能讲到 2.4.1. 自主决定每列的宽度 ...

    efs.rar_efs_efs java_ext 框架_html

    Efs 是企业快速开发的UI 层。这个UI 层封装extjs 框架,Ext 提供了好的 页面布局方式、功能强大的组件、优质的页面风格,但是如果直接使用Ext又是 ...当然你也可以使用new Ext 的控件渲染到 页面,两者可以混合使用。

    Ext+JS高级程序设计.rar

    第三部分 Ext-UI 第8章 Ext用户界面控件 216 8.1 布局 216 8.1.1 在FormPanel中使用HBoxLayout进行布局 216 8.1.2 在FormPanel中使用HBoxLayout和VBoxLayout进行布局 219 8.1.3 Panel的body的样式范围 224 8.2 Form...

    Ext 开发指南 学习资料

    让你知道ext表格控件的厉害。 2.1. 功能丰富,无人能出其右 2.2. 让我们搞一个grid出来耍耍吧。 2.3. grid默认自带的功能 2.4. 按顺序,咱们先要把常见功能讲到 2.4.1. 自主决定每列的宽度 2.4.2. 让grid支持按列...

    ext中文api文档

    ExtJS是一个跨浏览器,用于开发RIA(Rich iInternet Application)应用的JavaScript框架。提供:高性,可定制的Web UI控件库。良好的设计、丰富的文档和可扩展的组件模型。

    ext JS API 实战

    控件(widgets):控件是指可以直接在页面中创建的可视化组件,比如面板、选项板、表格、树、窗口、菜单、工具栏、按钮等等,在我们的应用程序中可以直接通过应用这些控件来实现友好、交互性强的应用程序的UI。控件...

    [.Net控件] Infragistics NetAdvantage for Windows UI 2013 Vol.1

    Deliver the latest Touch UI with our new gesture supported controls - like our Editor controls - without needing to write code separately for mouse vs. touchscreen inputs. Touch support across ...

    EXT.NET官方版

    Ext.NET是一个开源的ASP.NET(WebForm + MVC)组件,完美的集成了扩浏览器的js脚本库Ext JS,简单点说,就是一个让webform后台看起来更专业的组件。使用方法和学习请到官网http://www.ext.net/。上面有控件的使用及源...

    掏钱学Ext(完整版) 附全部源码

    让你知道ext表格控件的厉害。 2.1. 功能丰富,无人能出其右 2.2. 让我们搞一个grid出来耍耍吧。 2.3. 上边那个是1.x的,2.0稍微有些不同哦 2.4. 按顺序,咱们先要把常见功能讲到,让grid支持按列排序 2.5. 让...

    ExtAspNet控件 v3.1.9源码2012825

    基于 ExtJS 的专业 ASP.NET 2.0 控件库,拥有原生的 AJAX 支持和华丽的UI效果。 ExtAspNet的使命 创建没有 JavaScript,没有 CSS,没有 UpdatePanel,没有 ViewState,没有 WebServices 的网站应用程序。 支持的...

    基于Extjs的开源控件库ExtAspNet

    ExtAspNet 是一组专业的Asp.net控件库,拥有原生的AJAX支持和丰富的UI效果。 目标是创建没有JavaScript,没有CSS,没有UpdatePanel,没有WebServices的Web应用程序。 注:ExtAspNet 基于一些开源程序,比如ExtJS, ...

    Cocos2d-x UI开发之CCControlButton控件类实例

    对于游戏的开发,UI的开发同样需要控件来提高开发效率。对Cocos2D-x来说,从2.0版本开始提供了很多控件类来帮助我们更好地开发UI。 在HelloWorld.h中加入如下俩句代码 //需要包含如下的头文件和命名空间的申明 #...

Global site tag (gtag.js) - Google Analytics