可用于Flex的Flash组件
17:39 , Qizhi
如果想在Flex Actionscript工程里使用fl.*包下的组件,不妨使用这个SWC:
http://asform.googlecode.com/files/FlSWC.swc
我已经在其中包含了Yahoo Astra的系列Flash组件。
如果想仅仅使用部分其中的组件,可以在flash中先导入对应的组件,然后在Lib中右键,选择导出SWC即可。
当然,如果仅仅想使用Fl的UiComponent组件,可以到flash的安装目录下找到对应的源文件。但是在Flex Actionscript工程里直接使用其中的组件时,报错是必然的,因为皮肤文件没被导入:
TypeError: Error #2007: Parameter child must be non-null.
at flash.display::DisplayObjectContainer/addChildAt()
at fl.controls::BaseButton/fl.controls:BaseButton::drawBackground()[C:\Program Files\Adobe\Adobe Flash CS3\en\Configuration\Component Source\ActionScript 3.0\User Interface;fl\controls;BaseButton.as:538]
at fl.controls::LabelButton/fl.controls:LabelButton::draw()[C:\Program Files\Adobe\Adobe Flash CS3\en\Configuration\Component Source\ActionScript 3.0\User Interface;fl\controls;LabelButton.as:600]
at fl.controls::Button/fl.controls:Button::draw()[C:\Program Files\Adobe\Adobe Flash CS3\en\Configuration\Component Source\ActionScript 3.0\User Interface;fl\controls;Button.as:167]
at fl.core::UIComponent/fl.core:UIComponent::callLaterDispatcher()[C:\Program Files\Adobe\Adobe Flash CS3\en\Configuration\Component Source\ActionScript 3.0\User Interface;fl\core;UIComponent.as:1379]
at [renderEvent]
http://asform.googlecode.com/files/FlSWC.swc
我已经在其中包含了Yahoo Astra的系列Flash组件。
如果想仅仅使用部分其中的组件,可以在flash中先导入对应的组件,然后在Lib中右键,选择导出SWC即可。
当然,如果仅仅想使用Fl的UiComponent组件,可以到flash的安装目录下找到对应的源文件。但是在Flex Actionscript工程里直接使用其中的组件时,报错是必然的,因为皮肤文件没被导入:
TypeError: Error #2007: Parameter child must be non-null.
at flash.display::DisplayObjectContainer/addChildAt()
at fl.controls::BaseButton/fl.controls:BaseButton::drawBackground()[C:\Program Files\Adobe\Adobe Flash CS3\en\Configuration\Component Source\ActionScript 3.0\User Interface;fl\controls;BaseButton.as:538]
at fl.controls::LabelButton/fl.controls:LabelButton::draw()[C:\Program Files\Adobe\Adobe Flash CS3\en\Configuration\Component Source\ActionScript 3.0\User Interface;fl\controls;LabelButton.as:600]
at fl.controls::Button/fl.controls:Button::draw()[C:\Program Files\Adobe\Adobe Flash CS3\en\Configuration\Component Source\ActionScript 3.0\User Interface;fl\controls;Button.as:167]
at fl.core::UIComponent/fl.core:UIComponent::callLaterDispatcher()[C:\Program Files\Adobe\Adobe Flash CS3\en\Configuration\Component Source\ActionScript 3.0\User Interface;fl\core;UIComponent.as:1379]
at [renderEvent]
Apr
11
2009
用于纯Actionscript工程的轻量级Form组件集(二)
17:56 , Qizhi
如前一篇Blog所述,正式发布Form的beta版。这一版基本完成了框架的搭建,将部分常用的样式配置独立出来,并且支持通过在外部设置XML配置,动态的生成组件甚至是程序的个模块。这一版本包含的组件有:
1、控制类:多选/单选; 下拉框; 文本输入框; 列表; 标签; 按钮/状态按钮/链接按钮;
2、布局类:HBox/VBox;
3、其他:Alert; Popout;
示例:
Flash Player文件
使用了这套组件开发的程序示例(一款音乐播放器):
http://lads.myspace.cn/widget/form/light/lightplayer.html
项目地址:
http://code.google.com/p/asform/
目前没有直接提供下载,可以通过svn访问源代码。SWC位于http://asform.googlecode.com/svn/trunk/Form/src/swc/Form.swc;一个完整的Test示例位于http://asform.googlecode.com/svn/trunk/FormTest/src/FormTest.as。
此外,我会在一个月后从Myspace China离职,目前下家尚未确定,只是在和一些朋友、猎头沟通,因此这套组件库的更新也许会停下来一段时间。当然如果朋友们有不错的公司,是很欢迎给我引荐下的,再此谢过。
下面是简单的使用示例:
1、基础控件的使用,直接使用封装好的类即可。如果要改变样式,可以改变FormAsset.swf中的对应的样式即可。更高级的组件定制请见后文。:
var radio1:Radio = new Radio();
radio1.label = "radio 1";
radio1.formName = "Radio";
radio1.width = 65;
var radio2:Radio = new Radio();
radio2.label = "radio 2";
radio2.formName = "Radio";
radio2.width = 65;
var radio3:Radio = new Radio();
radio3.label = "radio 3";
radio3.formName = "Radio";
radio3.width = 65;
addChild(radio1);
addChild(radio2);
addChild(radio3);
2、如果要使用布局类,如HBox/VBox等实现随场景自动伸缩,其root需要指向ApplicationContainer这个类。ApplicationContainer封装了所有布局类、Alert、Popout的接口。
如:
package {
import flash.events.Event;
import flash.events.MouseEvent;
import form.ui.*
import form.ui.component.Layout.HBox;
[SWF(backgroundColor="0xFFFFFF", frameRate="30", width="450", height="400")]
public class FormTest extends ApplicationContainer
{
private var listData:Array;
public function FormTest()
{
super();
this.layout.paddingLeft = 10;
this.layout.paddingTop = 10;
runDefault();
}
private function delayTest(li:ListBase):void
{
for(var i:int = 300; i < 350; i++)
li.addItem({"label": i, "b": "act"});
}
public function runDefault():void
{
var checkBoxLabel:Label = new Label();
checkBoxLabel.formValue = "CheckBox Group";
checkBoxLabel.width = 200;
addChild(checkBoxLabel);
var checkBox1:CheckBox = new CheckBox();
checkBox1.label = "checkBox 1";
checkBox1.width = 70;
checkBox1.checkedOnAddedToStage = true;
var checkBox2:CheckBox = new CheckBox();
checkBox2.label = "checkBox 2";
checkBox2.width = 70;
var checkBoxBox:HBox = new HBox();
checkBoxBox.percentWidth = 100;
checkBoxBox.addChild(checkBox1);
checkBoxBox.addChild(checkBox2);
addChild(checkBoxBox);
var select:Select = new Select();
select.addHeader("Default Header");
select.addOption("Select Opinion1", "Select Opinion1");
select.addOption("Select Opinion2", "Select Opinion2");
select.prompt = "Select";
var input:Input = new Input();
input.prompt = "Input";
var button:FormButton = new FormButton();
button.label = "Button";
button.width = 80;
button.addEventListener(MouseEvent.CLICK, showAlert, false, 0, true);
var ioBox:HBox = new HBox();
ioBox.percentWidth = 100;
ioBox.horizontalGap = 10;
ioBox.addChild(select);
ioBox.addChild(input);
ioBox.addChild(button);
addChild(ioBox);
}
private function showAlert(e:Event):void
{
Alert.show("Alert Title", "Alert Message");
}
}
}
3、最强大的部分:这套组件建立的核心是快速应对变化的需求,以不变(组件核心库)应万变(只改变配置文件)。下面先看一段XML:
<?xml version="1.0" encoding="utf-8" ?>
<layout>
<layoutType>boxLayout</layoutType>
<url>assets/blackAsset.swf</url>
<global>
<item name="cn.myspace.player.ui.DefaultTrackListRender" style="index-text-color: #FFFFFF; track-title-color:#FFFFFF; "/>
</global>
<data>
<app style="padding: 5px; vertical-gap:5px; background:bgClip_linkage; background-size:100%;">
<hbox style="width:100%; height:35px; padding-top:2px; padding-bottom:5px;">
<logo style="background:logo; width:100%;"/>
</hbox>
</app>
</data>
</layout>
传统的Flex中,样式(Skin)是可以编译成SWF从而实现在RunTime换肤。而这套组件就没必要重复造轮子了。
以播放器为例,播放器出去一些控制按钮、均衡器、波形表等基础组件外,再也没有其他的可变的部分了。这是“不变”的根本。变化的是这些部件的外观和各种组合方式。如果他们能写到配置文件里...是的,以后要做的就不再是开发,而是维护工作了。这边是上面这段XML做的事情。完整的皮肤配置文件位于http://lads.myspace.cn/widget/form/light/assets/userBlank.xml.
实际上 每个项目有一个组件工厂(必须是每个项目都各不相同,且很难定义一个统一的接口,所以不包含在Form库中。),工厂负责装配这些配置文件里的各个模块,它形如:
public class UIFactory
{
public static function createUI(xml:XML):FMSprite
{
var name:String = xml.name().localName;
var box:FMSprite;
name = StringUtils.trim(name).toLowerCase();
switch (name)
{
case "app": box = ApplicationContainer.application; break;
case "logo": box = new Logo; break;
case "hbox": box = new HBox(); break;
case "vbox": box = new VBox(); break;
case "box": box = new Box(); break;
case "art": box = new Art(); break;
...
}
var css:CSS = CssFactory.createCSS(box);
css.fromXML(xml);
return box;
}
}
组件库有一个CSS分析器将所有XML组件元素标签的style属性转化为Flash的Class,然后由StyleManager类来管理这个组件的皮肤设置。
当然,这版还没有完成的一部分是List的ItemRender的动态配置。CSS的模块不一定每个项目都会用到,所以他和各个模块不存在依赖关系,实际项目中用到的时候才会import他们,尽管这个模块没多大。
更好的方式是,主程序只做容器,读取到配置文件都再决定加载它们,最后设定他们的外观——很多大型项目不都是基于这一思想实现的么?
播放器的代码由于是公司里的项目所以不便公开,这里只能给出一个大致的实现思想,很多人都可以顺着这个思想实现他。当然,这中想法在Flex里也是可以实现的。
1、控制类:多选/单选; 下拉框; 文本输入框; 列表; 标签; 按钮/状态按钮/链接按钮;
2、布局类:HBox/VBox;
3、其他:Alert; Popout;
示例:
Flash Player文件使用了这套组件开发的程序示例(一款音乐播放器):
http://lads.myspace.cn/widget/form/light/lightplayer.html
项目地址:
http://code.google.com/p/asform/
目前没有直接提供下载,可以通过svn访问源代码。SWC位于http://asform.googlecode.com/svn/trunk/Form/src/swc/Form.swc;一个完整的Test示例位于http://asform.googlecode.com/svn/trunk/FormTest/src/FormTest.as。
此外,我会在一个月后从Myspace China离职,目前下家尚未确定,只是在和一些朋友、猎头沟通,因此这套组件库的更新也许会停下来一段时间。当然如果朋友们有不错的公司,是很欢迎给我引荐下的,再此谢过。
下面是简单的使用示例:
1、基础控件的使用,直接使用封装好的类即可。如果要改变样式,可以改变FormAsset.swf中的对应的样式即可。更高级的组件定制请见后文。:
var radio1:Radio = new Radio();
radio1.label = "radio 1";
radio1.formName = "Radio";
radio1.width = 65;
var radio2:Radio = new Radio();
radio2.label = "radio 2";
radio2.formName = "Radio";
radio2.width = 65;
var radio3:Radio = new Radio();
radio3.label = "radio 3";
radio3.formName = "Radio";
radio3.width = 65;
addChild(radio1);
addChild(radio2);
addChild(radio3);
2、如果要使用布局类,如HBox/VBox等实现随场景自动伸缩,其root需要指向ApplicationContainer这个类。ApplicationContainer封装了所有布局类、Alert、Popout的接口。
如:
package {
import flash.events.Event;
import flash.events.MouseEvent;
import form.ui.*
import form.ui.component.Layout.HBox;
[SWF(backgroundColor="0xFFFFFF", frameRate="30", width="450", height="400")]
public class FormTest extends ApplicationContainer
{
private var listData:Array;
public function FormTest()
{
super();
this.layout.paddingLeft = 10;
this.layout.paddingTop = 10;
runDefault();
}
private function delayTest(li:ListBase):void
{
for(var i:int = 300; i < 350; i++)
li.addItem({"label": i, "b": "act"});
}
public function runDefault():void
{
var checkBoxLabel:Label = new Label();
checkBoxLabel.formValue = "CheckBox Group";
checkBoxLabel.width = 200;
addChild(checkBoxLabel);
var checkBox1:CheckBox = new CheckBox();
checkBox1.label = "checkBox 1";
checkBox1.width = 70;
checkBox1.checkedOnAddedToStage = true;
var checkBox2:CheckBox = new CheckBox();
checkBox2.label = "checkBox 2";
checkBox2.width = 70;
var checkBoxBox:HBox = new HBox();
checkBoxBox.percentWidth = 100;
checkBoxBox.addChild(checkBox1);
checkBoxBox.addChild(checkBox2);
addChild(checkBoxBox);
var select:Select = new Select();
select.addHeader("Default Header");
select.addOption("Select Opinion1", "Select Opinion1");
select.addOption("Select Opinion2", "Select Opinion2");
select.prompt = "Select";
var input:Input = new Input();
input.prompt = "Input";
var button:FormButton = new FormButton();
button.label = "Button";
button.width = 80;
button.addEventListener(MouseEvent.CLICK, showAlert, false, 0, true);
var ioBox:HBox = new HBox();
ioBox.percentWidth = 100;
ioBox.horizontalGap = 10;
ioBox.addChild(select);
ioBox.addChild(input);
ioBox.addChild(button);
addChild(ioBox);
}
private function showAlert(e:Event):void
{
Alert.show("Alert Title", "Alert Message");
}
}
}
3、最强大的部分:这套组件建立的核心是快速应对变化的需求,以不变(组件核心库)应万变(只改变配置文件)。下面先看一段XML:
<?xml version="1.0" encoding="utf-8" ?>
<layout>
<layoutType>boxLayout</layoutType>
<url>assets/blackAsset.swf</url>
<global>
<item name="cn.myspace.player.ui.DefaultTrackListRender" style="index-text-color: #FFFFFF; track-title-color:#FFFFFF; "/>
</global>
<data>
<app style="padding: 5px; vertical-gap:5px; background:bgClip_linkage; background-size:100%;">
<hbox style="width:100%; height:35px; padding-top:2px; padding-bottom:5px;">
<logo style="background:logo; width:100%;"/>
</hbox>
</app>
</data>
</layout>
传统的Flex中,样式(Skin)是可以编译成SWF从而实现在RunTime换肤。而这套组件就没必要重复造轮子了。
以播放器为例,播放器出去一些控制按钮、均衡器、波形表等基础组件外,再也没有其他的可变的部分了。这是“不变”的根本。变化的是这些部件的外观和各种组合方式。如果他们能写到配置文件里...是的,以后要做的就不再是开发,而是维护工作了。这边是上面这段XML做的事情。完整的皮肤配置文件位于http://lads.myspace.cn/widget/form/light/assets/userBlank.xml.
实际上 每个项目有一个组件工厂(必须是每个项目都各不相同,且很难定义一个统一的接口,所以不包含在Form库中。),工厂负责装配这些配置文件里的各个模块,它形如:
public class UIFactory
{
public static function createUI(xml:XML):FMSprite
{
var name:String = xml.name().localName;
var box:FMSprite;
name = StringUtils.trim(name).toLowerCase();
switch (name)
{
case "app": box = ApplicationContainer.application; break;
case "logo": box = new Logo; break;
case "hbox": box = new HBox(); break;
case "vbox": box = new VBox(); break;
case "box": box = new Box(); break;
case "art": box = new Art(); break;
...
}
var css:CSS = CssFactory.createCSS(box);
css.fromXML(xml);
return box;
}
}
组件库有一个CSS分析器将所有XML组件元素标签的style属性转化为Flash的Class,然后由StyleManager类来管理这个组件的皮肤设置。
当然,这版还没有完成的一部分是List的ItemRender的动态配置。CSS的模块不一定每个项目都会用到,所以他和各个模块不存在依赖关系,实际项目中用到的时候才会import他们,尽管这个模块没多大。
更好的方式是,主程序只做容器,读取到配置文件都再决定加载它们,最后设定他们的外观——很多大型项目不都是基于这一思想实现的么?
播放器的代码由于是公司里的项目所以不便公开,这里只能给出一个大致的实现思想,很多人都可以顺着这个思想实现他。当然,这中想法在Flex里也是可以实现的。
Feb
26
2009
用于纯Actionscript工程的轻量级Form组件集
18:30 , Qizhi
Flex太大,于是写了下面的轻量级Form控件,我会逐渐丰富、完善,直至...开源的那一天。
优点:轻量级(<50K);支持自定义皮肤;支持Runtime换肤...
在不久的将来会发布beta版,同时开放源代码。
Flash Player文件
下面是例子的主要代码:
package {
import flash.events.Event;
import flash.events.MouseEvent;
import flash.system.Security;
import form.ui.Alert;
import form.ui.ApplicationContainer;
import form.ui.CheckBox;
import form.ui.FormButton;
import form.ui.HSlider;
import form.ui.Input;
import form.ui.Label;
import form.ui.Radio;
import form.ui.Select;
import form.ui.Spacer;
import form.ui.VSlider;
import form.ui.component.Layout.Box;
import form.ui.component.Layout.HBox;
[SWF(backgroundColor="0xFFFFFF", frameRate="30", width="450", height="300")]
public class FormTest extends ApplicationContainer
{
public function FormTest()
{
Security.allowDomain("*");
super(Box.VERTICAL)
this.percentHeight = 100;
runDefault();
/*
//StyleManager.getInstance().addEventListener(StyleEvent.STYLE_COMPLETE, onStyleLoad);
StyleManager.getInstance().loadStyleDeclarations("assets/blackAsset.swf"); */
}
public function runDefault():void
{
var radioLabel:Label = new Label();
radioLabel.formValue = "Radio Group";
radioLabel.width = 200;
addChild(radioLabel);
var radio1:Radio = new Radio();
radio1.label = "radio 1";
radio1.formName = "Radio";
radio1.width = 65;
var radio2:Radio = new Radio();
radio2.label = "radio 2";
radio2.formName = "Radio";
radio2.width = 65;
var radio3:Radio = new Radio();
radio3.label = "radio 3";
radio3.formName = "Radio";
radio3.width = 65;
var radio4:Radio = new Radio();
radio4.label = "radio 4";
radio4.formName = "Radio";
radio4.width = 65;
var radioBox:HBox = new HBox();
radioBox.percentWidth = 100;
radioBox.addChild(radio1);
radioBox.addChild(radio2);
radioBox.addChild(radio3);
radioBox.addChild(radio4);
addChild(radioBox);
var sp:Spacer = new Spacer();
sp.height = 10;
addChild(sp);
var checkBoxLabel:Label = new Label();
checkBoxLabel.formValue = "CheckBox Group";
checkBoxLabel.width = 200;
addChild(checkBoxLabel);
var checkBox1:CheckBox = new CheckBox();
checkBox1.label = "checkBox 1";
checkBox1.width = 70;
checkBox1.checkedOnAddedToStage = true;
var checkBox2:CheckBox = new CheckBox();
checkBox2.label = "checkBox 2";
checkBox2.width = 70;
var checkBox3:CheckBox = new CheckBox();
checkBox3.label = "checkBox 3";
checkBox3.width = 70;
var checkBox4:CheckBox = new CheckBox();
checkBox4.label = "checkBox 4";
checkBox4.width = 70;
var checkBoxBox:HBox = new HBox();
checkBoxBox.percentWidth = 100;
checkBoxBox.addChild(checkBox1);
checkBoxBox.addChild(checkBox2);
checkBoxBox.addChild(checkBox3);
checkBoxBox.addChild(checkBox4);
addChild(checkBoxBox);
addChild(sp);
var select:Select = new Select();
select.addHeader("Default Header");
select.addOption("Select Opinion1", "Select Opinion1");
select.addOption("Select Opinion2", "Select Opinion2");
select.addOption("Select Opinion3", "Select Opinion3");
select.addOption("Select Opinion4", "Select Opinion4");
select.addOption("Select Opinion5", "Select Opinion5");
select.addHeader("Second Header");
select.addOption("Select Opinion6", "Select Opinion6");
select.addOption("Select Opinion7", "Select Opinion7");
select.addOption("Select Opinion8", "Select Opinion8");
select.addOption("Select Opinion9", "Select Opinion9");
select.addSeparator();
select.addOption("Select Opinion10", "Select Opinion10");
select.addOption("Select Opinion11", "Select Opinion11");
select.prompt = "Select";
var input:Input = new Input();
input.prompt = "Input";
var button:FormButton = new FormButton();
button.label = "Button";
button.width = 80;
button.addEventListener(MouseEvent.CLICK, showAlert, false, 0, true);
var ioBox:HBox = new HBox();
ioBox.percentWidth = 100;
ioBox.horizontalGap = 10;
ioBox.addChild(select);
ioBox.addChild(input);
ioBox.addChild(button);
addChild(ioBox);
var hslide:HSlider = new HSlider();
hslide.value = 0.5;
addChild(hslide);
var box:Box = new Box(Box.ABSOLUTE);
box.percentWidth = 100;
box.height = 200;
addChild(box);
var vslide:VSlider = new VSlider();
vslide.y = 120;
vslide.value = 0.2;
vslide.setProgress(0.5);
box.addChild(vslide);
this.invalidateDisplayList(true);
}
public function onStyleLoad(e:Event):void
{
trace(e);
}
private function showAlert(e:Event):void
{
Alert.show("Alert Title", "Alert Message");
}
}
}
优点:轻量级(<50K);支持自定义皮肤;支持Runtime换肤...
在不久的将来会发布beta版,同时开放源代码。
Flash Player文件下面是例子的主要代码:
package {
import flash.events.Event;
import flash.events.MouseEvent;
import flash.system.Security;
import form.ui.Alert;
import form.ui.ApplicationContainer;
import form.ui.CheckBox;
import form.ui.FormButton;
import form.ui.HSlider;
import form.ui.Input;
import form.ui.Label;
import form.ui.Radio;
import form.ui.Select;
import form.ui.Spacer;
import form.ui.VSlider;
import form.ui.component.Layout.Box;
import form.ui.component.Layout.HBox;
[SWF(backgroundColor="0xFFFFFF", frameRate="30", width="450", height="300")]
public class FormTest extends ApplicationContainer
{
public function FormTest()
{
Security.allowDomain("*");
super(Box.VERTICAL)
this.percentHeight = 100;
runDefault();
/*
//StyleManager.getInstance().addEventListener(StyleEvent.STYLE_COMPLETE, onStyleLoad);
StyleManager.getInstance().loadStyleDeclarations("assets/blackAsset.swf"); */
}
public function runDefault():void
{
var radioLabel:Label = new Label();
radioLabel.formValue = "Radio Group";
radioLabel.width = 200;
addChild(radioLabel);
var radio1:Radio = new Radio();
radio1.label = "radio 1";
radio1.formName = "Radio";
radio1.width = 65;
var radio2:Radio = new Radio();
radio2.label = "radio 2";
radio2.formName = "Radio";
radio2.width = 65;
var radio3:Radio = new Radio();
radio3.label = "radio 3";
radio3.formName = "Radio";
radio3.width = 65;
var radio4:Radio = new Radio();
radio4.label = "radio 4";
radio4.formName = "Radio";
radio4.width = 65;
var radioBox:HBox = new HBox();
radioBox.percentWidth = 100;
radioBox.addChild(radio1);
radioBox.addChild(radio2);
radioBox.addChild(radio3);
radioBox.addChild(radio4);
addChild(radioBox);
var sp:Spacer = new Spacer();
sp.height = 10;
addChild(sp);
var checkBoxLabel:Label = new Label();
checkBoxLabel.formValue = "CheckBox Group";
checkBoxLabel.width = 200;
addChild(checkBoxLabel);
var checkBox1:CheckBox = new CheckBox();
checkBox1.label = "checkBox 1";
checkBox1.width = 70;
checkBox1.checkedOnAddedToStage = true;
var checkBox2:CheckBox = new CheckBox();
checkBox2.label = "checkBox 2";
checkBox2.width = 70;
var checkBox3:CheckBox = new CheckBox();
checkBox3.label = "checkBox 3";
checkBox3.width = 70;
var checkBox4:CheckBox = new CheckBox();
checkBox4.label = "checkBox 4";
checkBox4.width = 70;
var checkBoxBox:HBox = new HBox();
checkBoxBox.percentWidth = 100;
checkBoxBox.addChild(checkBox1);
checkBoxBox.addChild(checkBox2);
checkBoxBox.addChild(checkBox3);
checkBoxBox.addChild(checkBox4);
addChild(checkBoxBox);
addChild(sp);
var select:Select = new Select();
select.addHeader("Default Header");
select.addOption("Select Opinion1", "Select Opinion1");
select.addOption("Select Opinion2", "Select Opinion2");
select.addOption("Select Opinion3", "Select Opinion3");
select.addOption("Select Opinion4", "Select Opinion4");
select.addOption("Select Opinion5", "Select Opinion5");
select.addHeader("Second Header");
select.addOption("Select Opinion6", "Select Opinion6");
select.addOption("Select Opinion7", "Select Opinion7");
select.addOption("Select Opinion8", "Select Opinion8");
select.addOption("Select Opinion9", "Select Opinion9");
select.addSeparator();
select.addOption("Select Opinion10", "Select Opinion10");
select.addOption("Select Opinion11", "Select Opinion11");
select.prompt = "Select";
var input:Input = new Input();
input.prompt = "Input";
var button:FormButton = new FormButton();
button.label = "Button";
button.width = 80;
button.addEventListener(MouseEvent.CLICK, showAlert, false, 0, true);
var ioBox:HBox = new HBox();
ioBox.percentWidth = 100;
ioBox.horizontalGap = 10;
ioBox.addChild(select);
ioBox.addChild(input);
ioBox.addChild(button);
addChild(ioBox);
var hslide:HSlider = new HSlider();
hslide.value = 0.5;
addChild(hslide);
var box:Box = new Box(Box.ABSOLUTE);
box.percentWidth = 100;
box.height = 200;
addChild(box);
var vslide:VSlider = new VSlider();
vslide.y = 120;
vslide.value = 0.2;
vslide.setProgress(0.5);
box.addChild(vslide);
this.invalidateDisplayList(true);
}
public function onStyleLoad(e:Event):void
{
trace(e);
}
private function showAlert(e:Event):void
{
Alert.show("Alert Title", "Alert Message");
}
}
}
Feb
16
2009
在Appengine中使用AMF通信
18:50 , Qizhi
在试用了XML、JSON、AMF3后,最终不得不折服于AMF的高效率,AMF是个好东西,优点就不啰嗦了。在Google App Engine中使用AMF通信其实也很简单,只要加入PyAMF库即可。
1、Python端:
Echo的示例可以参考pyamf的官方文档。下面的例子展示了如何运用PyAMF访问数据库并序列号后返回给Flash端:
import datetime
import wsgiref.handlers
from pyamf.remoting.gateway.wsgi import WSGIGateway
from google.appengine.ext import db
def GetOwnerFlowers(platform, oid, max):
flowers = FlowerModel.all().filter('pid =', platform).filter('oid =', oid).order('-date').fetch(max)
return formatList(flowers)
def GetViewerFlowers(platform, oid, vid, max):
flowers = FlowerModel.all().filter('pid =', platform).filter('vid =', vid).filter('oid =', oid).order('-date').fetch(max)
return formatList(flowers)
def SaveFlower(data):
flower = FlowerModel()
flower.oid = data['oid']
flower.oname = data['oname']
flower.date = datetime.datetime.utcnow()
flower.put()
return True
def formatList(flowers, userCount, sysCount):
list = []
for flower in flowers:
result = {'oid':'', 'oname':'', 'date':''}
result['oid'] = flower.oid
result['oname'] = flower.oname
result['date'] = flower.date
list.append(result)
return list
services = {
'Garden.GetOwnerFlowers': GetOwnerFlowers,
'Garden.GetViewerFlowers': GetViewerFlowers,
'Garden.SaveFlower': SaveFlower
}
class FlowerModel(db.Model):
oid = db.StringProperty() #owner id
oname = db.StringProperty() #owner name
date = db.DateTimeProperty() #created date
def main():
application = WSGIGateway(services)
wsgiref.handlers.CGIHandler().run(application)
if __name__ == '__main__':
main()
2、Flash端不用做任何序列号的操作:
package com.moorwind.fans.utils
{
import com.moorwind.fans.core.Constant;
import com.moorwind.fans.core.Context;
import com.moorwind.fans.events.ServiceEvent;
import com.moorwind.fans.vo.FlowerVO;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IOErrorEvent;
import flash.events.SecurityErrorEvent;
import flash.net.NetConnection;
import flash.net.Responder;
public class ServiceUtils extends EventDispatcher
{
private var conn:NetConnection = new NetConnection();
public function ServiceUtils()
{
conn.connect(Constant.END_POINT);
conn.addEventListener(IOErrorEvent.IO_ERROR, onConnectError);
conn.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onConnectError);
}
public function getOwner(oid:String, pid:int):void
{
var responder:Responder = new Responder(onGetOwner, onDataError);
conn.call(Constant.GET_OWNERS, responder, pid, oid, Constant.LIMIT);
}
public function getViewer(oid:String, vid:String, pid:int):void
{
var responder:Responder = new Responder(onGetViewer, onDataError);
conn.call(Constant.GET_VIEWERS, responder, pid, oid, vid, Constant.LIMIT);
}
public function saveFlower(flower:FlowerVO):void
{
var responder:Responder = new Responder(onSaveData, onDataError);
conn.call(Constant.SAVE_FLOWERS, responder, flower);
}
private function onGetOwner(result:Object):void
{
Context.instance.ownerTotal = result.userCount;
Context.instance.sysTotal = result.sysCount;
var arr:Array = [];
for each(var obj:Object in result.flowers)
{
var fl:FlowerVO = new FlowerVO();
fl.oid = obj["oid"];
fl.oname = obj["oname"];
arr.push(fl);
}
var evt:ServiceEvent = new ServiceEvent(ServiceEvent.FLOWER_OWNER);
evt.flowers = arr;
dispatchEvent(evt);
}
private function onGetViewer(result:Array):void
{
var arr:Array = [];
for each(var obj:Object in result)
{
var fl:FlowerVO = new FlowerVO();
fl.oid = obj["oid"];
fl.oname = obj["oname"];
arr.push(fl);
}
var evt:ServiceEvent = new ServiceEvent(ServiceEvent.FLOWER_VIEWER);
evt.flowers = arr;
dispatchEvent(evt);
}
private function onSaveData(result:Boolean):void
{
if(result)
{
dispatchEvent(new ServiceEvent(ServiceEvent.FLOWER_SAVED));
}
}
private function onDataError(result:Object):void
{
trace(result);
}
public function onConnectError(e:Event):void
{
trace(e);
}
}
}
唯一不好的是。。。使用PYAMF占用CPU太高,哎
1、Python端:
Echo的示例可以参考pyamf的官方文档。下面的例子展示了如何运用PyAMF访问数据库并序列号后返回给Flash端:
import datetime
import wsgiref.handlers
from pyamf.remoting.gateway.wsgi import WSGIGateway
from google.appengine.ext import db
def GetOwnerFlowers(platform, oid, max):
flowers = FlowerModel.all().filter('pid =', platform).filter('oid =', oid).order('-date').fetch(max)
return formatList(flowers)
def GetViewerFlowers(platform, oid, vid, max):
flowers = FlowerModel.all().filter('pid =', platform).filter('vid =', vid).filter('oid =', oid).order('-date').fetch(max)
return formatList(flowers)
def SaveFlower(data):
flower = FlowerModel()
flower.oid = data['oid']
flower.oname = data['oname']
flower.date = datetime.datetime.utcnow()
flower.put()
return True
def formatList(flowers, userCount, sysCount):
list = []
for flower in flowers:
result = {'oid':'', 'oname':'', 'date':''}
result['oid'] = flower.oid
result['oname'] = flower.oname
result['date'] = flower.date
list.append(result)
return list
services = {
'Garden.GetOwnerFlowers': GetOwnerFlowers,
'Garden.GetViewerFlowers': GetViewerFlowers,
'Garden.SaveFlower': SaveFlower
}
class FlowerModel(db.Model):
oid = db.StringProperty() #owner id
oname = db.StringProperty() #owner name
date = db.DateTimeProperty() #created date
def main():
application = WSGIGateway(services)
wsgiref.handlers.CGIHandler().run(application)
if __name__ == '__main__':
main()
2、Flash端不用做任何序列号的操作:
package com.moorwind.fans.utils
{
import com.moorwind.fans.core.Constant;
import com.moorwind.fans.core.Context;
import com.moorwind.fans.events.ServiceEvent;
import com.moorwind.fans.vo.FlowerVO;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IOErrorEvent;
import flash.events.SecurityErrorEvent;
import flash.net.NetConnection;
import flash.net.Responder;
public class ServiceUtils extends EventDispatcher
{
private var conn:NetConnection = new NetConnection();
public function ServiceUtils()
{
conn.connect(Constant.END_POINT);
conn.addEventListener(IOErrorEvent.IO_ERROR, onConnectError);
conn.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onConnectError);
}
public function getOwner(oid:String, pid:int):void
{
var responder:Responder = new Responder(onGetOwner, onDataError);
conn.call(Constant.GET_OWNERS, responder, pid, oid, Constant.LIMIT);
}
public function getViewer(oid:String, vid:String, pid:int):void
{
var responder:Responder = new Responder(onGetViewer, onDataError);
conn.call(Constant.GET_VIEWERS, responder, pid, oid, vid, Constant.LIMIT);
}
public function saveFlower(flower:FlowerVO):void
{
var responder:Responder = new Responder(onSaveData, onDataError);
conn.call(Constant.SAVE_FLOWERS, responder, flower);
}
private function onGetOwner(result:Object):void
{
Context.instance.ownerTotal = result.userCount;
Context.instance.sysTotal = result.sysCount;
var arr:Array = [];
for each(var obj:Object in result.flowers)
{
var fl:FlowerVO = new FlowerVO();
fl.oid = obj["oid"];
fl.oname = obj["oname"];
arr.push(fl);
}
var evt:ServiceEvent = new ServiceEvent(ServiceEvent.FLOWER_OWNER);
evt.flowers = arr;
dispatchEvent(evt);
}
private function onGetViewer(result:Array):void
{
var arr:Array = [];
for each(var obj:Object in result)
{
var fl:FlowerVO = new FlowerVO();
fl.oid = obj["oid"];
fl.oname = obj["oname"];
arr.push(fl);
}
var evt:ServiceEvent = new ServiceEvent(ServiceEvent.FLOWER_VIEWER);
evt.flowers = arr;
dispatchEvent(evt);
}
private function onSaveData(result:Boolean):void
{
if(result)
{
dispatchEvent(new ServiceEvent(ServiceEvent.FLOWER_SAVED));
}
}
private function onDataError(result:Object):void
{
trace(result);
}
public function onConnectError(e:Event):void
{
trace(e);
}
}
}
唯一不好的是。。。使用PYAMF占用CPU太高,哎
Jan
20
2009
Appengine中的JSON序列化
13:42 , Qizhi
Simplejson是不能直接把Appengine中的db.Model序列化成JSON的,我是这么做的:
Model模块做如下扩展:
def getter(func):
if not func.__name__.startswith("get_"):
raise InvalidMethodName("method name must start with 'get_'")
func.getter = True
return func
class Resource(UserDict.DictMixin):
def exposed_attrs(self):
"""attribute names to be exposed as keys"""
return []
def hidden_keys(self):
"""Keys to hide from iteration. A Key will still be accessible if it is
a getter or if it is listed in exposed_attrs()"""
return []
def child_object(self, name):
"""Called to get a child object if it isn't found as a getter or an attribute"""
raise AttributeError
# Dictionary Methods
def __getitem__(self, key):
"""Called to implement evaluation of self[key]"""
getter = getattr(self, 'get_'+key, None)
if hasattr(getter, 'getter'):
return getter()
if key in self.exposed_attrs():
return getattr(self, key)
return self.child_object(key)
def __setitem__(self, key, value):
"""Called to implement assignment to self[key]"""
setter = getattr(self, 'set_'+key, None)
if hasattr(setter, 'setter'):
return setter(value)
def __delitem__(self, key):
"""Called to implement deletion of self[key]"""
deleter = getattr(self, 'del_'+key, None)
if hasattr(deleter, 'deleter'):
return deleter(value)
def keys(self):
"""List of keys for iteration"""
getter_keys = [name.replace('get_', '', 1) for name in dir(self)
if hasattr(getattr(self, name, None), 'getter')]
exposed_keys = getter_keys + self.exposed_attrs()
return [key for key in exposed_keys if key not in self.hidden_keys()]
当继承db.Model时,同时继承Resource:
class PersonModel(Resource, db.Model):
user_id = db.StringProperty()
user_content = db.TextProperty()
@getter
def get_uid(self):
return self.user_id
@getter
def get_content(self):
return self.user_content
在处理请求的时候,再做如下处理:
class RestHandler(webapp.RequestHandler):
def post(self):
self.dispatch_request()
def dispatch_request(self):
self.response.headers["Content-Type"] = "text/plain"
params = {}
for key in self.request.params:
params[str(key)] = self.request.get(key)
obj = //fetch data from PersonModel.
obj = self.preserialize(obj)
out = simplejson.dumps(obj)
self.response.out.write(out)
def translate(self, obj, depth):
return obj
def preserialize(self, obj, depth=0):
obj = self.translate(obj, depth)
if isinstance(obj, datetime.datetime):
return obj.isoformat()
elif isinstance(obj, db.users.User):
return obj.nickname()
elif isinstance(obj, (db.GeoPt, db.IM, db.Key)):
return str(obj)
elif isinstance(obj, types.ListType):
return [self.preserialize(item, depth+1) for item in obj]
elif isinstance(obj, (types.DictType, Resource)):
copy = {}
for key in obj:
copy[key] = self.preserialize(obj[key], depth+1)
return copy
return obj
这个方法不是太好,还有很大的优化空间...
Model模块做如下扩展:
def getter(func):
if not func.__name__.startswith("get_"):
raise InvalidMethodName("method name must start with 'get_'")
func.getter = True
return func
class Resource(UserDict.DictMixin):
def exposed_attrs(self):
"""attribute names to be exposed as keys"""
return []
def hidden_keys(self):
"""Keys to hide from iteration. A Key will still be accessible if it is
a getter or if it is listed in exposed_attrs()"""
return []
def child_object(self, name):
"""Called to get a child object if it isn't found as a getter or an attribute"""
raise AttributeError
# Dictionary Methods
def __getitem__(self, key):
"""Called to implement evaluation of self[key]"""
getter = getattr(self, 'get_'+key, None)
if hasattr(getter, 'getter'):
return getter()
if key in self.exposed_attrs():
return getattr(self, key)
return self.child_object(key)
def __setitem__(self, key, value):
"""Called to implement assignment to self[key]"""
setter = getattr(self, 'set_'+key, None)
if hasattr(setter, 'setter'):
return setter(value)
def __delitem__(self, key):
"""Called to implement deletion of self[key]"""
deleter = getattr(self, 'del_'+key, None)
if hasattr(deleter, 'deleter'):
return deleter(value)
def keys(self):
"""List of keys for iteration"""
getter_keys = [name.replace('get_', '', 1) for name in dir(self)
if hasattr(getattr(self, name, None), 'getter')]
exposed_keys = getter_keys + self.exposed_attrs()
return [key for key in exposed_keys if key not in self.hidden_keys()]
当继承db.Model时,同时继承Resource:
class PersonModel(Resource, db.Model):
user_id = db.StringProperty()
user_content = db.TextProperty()
@getter
def get_uid(self):
return self.user_id
@getter
def get_content(self):
return self.user_content
在处理请求的时候,再做如下处理:
class RestHandler(webapp.RequestHandler):
def post(self):
self.dispatch_request()
def dispatch_request(self):
self.response.headers["Content-Type"] = "text/plain"
params = {}
for key in self.request.params:
params[str(key)] = self.request.get(key)
obj = //fetch data from PersonModel.
obj = self.preserialize(obj)
out = simplejson.dumps(obj)
self.response.out.write(out)
def translate(self, obj, depth):
return obj
def preserialize(self, obj, depth=0):
obj = self.translate(obj, depth)
if isinstance(obj, datetime.datetime):
return obj.isoformat()
elif isinstance(obj, db.users.User):
return obj.nickname()
elif isinstance(obj, (db.GeoPt, db.IM, db.Key)):
return str(obj)
elif isinstance(obj, types.ListType):
return [self.preserialize(item, depth+1) for item in obj]
elif isinstance(obj, (types.DictType, Resource)):
copy = {}
for key in obj:
copy[key] = self.preserialize(obj[key], depth+1)
return copy
return obj
这个方法不是太好,还有很大的优化空间...
Jan
17
2009




