Flex the world

Flex, AIR and Things......

有朋友问到这个问题,这个问题主要涉及到dataProvider的操作,比较有针对性,所以写做教程。

这篇教程的核心是,Grid如果数据并不是操作Grid,而是操作它的dataProvider, Grid只是显示数据的载体

Demo操作:

1. 开始显示从A到Z的国家列表。可以前后翻页,每页显示10条
2. 从search框中输入搜索信息,grid将显示含有搜索字母的所有城市.
3. 同样可以对search结果进行翻页。



主要方法:

调用这个方法根据currentPage和searchedData设置Grid显示当前页

private function setCurrentPage():void{
currentPageData = new XMLListCollection(); //清空数据
var startIndex:int = currentPage * pageSize - pageSize; //开始位置
var endIndex:int = currentPage * pageSize;//结束位置
if(endIndex >= searchedData.length){ //如果结束位置大于search data的长度,结束位置为search data的长度
endIndex = searchedData.length;
this.lastPage = this.currentPage;
}
for(var i:int = startIndex; i< style="color: rgb(0, 0, 204);">/设置当前页数据

currentPageData.addItem(searchedData[i]);
}
}


调用此方法查询从 allData中查询数据,并显示第一页

private function doSearch():void{
this.searchedData = new XMLListCollection() //清空数据
for each(var item:XML in allData){ //从allData中搜索数据,将搜索到的数据添加到searchData
var value:String =item.@name.toString()
if(value.indexOf(searchValue.text) != -1){
this.searchedData.addItem(item);
}
}
this.currentPage = 1; //显示第一页
this.setCurrentPage();
}





源文件下载

这个demo是对上一个DataGrid demo的修改,目的是为了实现控制checkBox itemrender全选所有项,然后删除所有项。

在 上一个版本中,我在ICheckBox.as的set data function中根据当前value选中checkbox并且将选中的value push到selectedItems array中,这里有一个问题是grid不会初始化被scrollbar 隐藏的项 (谢谢lianyedie找到问题),所以会导致一个问题,没有显示部分的选中的item没有被正确加入到selectedItems array中。这个版本中我主要简化了iCheckBox.as, 不在setvlaue中设置选中项(同时也防止了重复插入选中项的问题)。


Demo内容:

1. 点击check box选中item,或点击全选,选中所有项。
2. 点击删除选中项,选中项的 status将变成 Deleted.



iCheckBox.as

package
{
import flash.events.Event;

import mx.controls.CheckBox;
import mx.core.Application;



public class iCheckBox extends CheckBox
{
private var currentData:Object; //保存当前一行值的对象

public function iCheckBox()
{
super();
this.addEventListener(Event.CHANGE,changeHandle)
}

override public function set data(value:Object):void{
this.selected = value.action.toString() == "true"?true:false;
this.currentData = value; //保存整行的引用
}
//点击check box时,根据状况向selectedItems array中添加当前行的引用,或者从array中移除
private function changeHandle(e:Event):void{
var itemArray:Array = Application.application.selectedItems
this.currentData.action = this.selected.toString()
if(this.selected){
itemArray.push(this.currentData)
}else{
for(var i:int = 0; i
<itemArray.length; i++){
if(itemArray[i] == this.currentData){
itemArray.splice(i,1)
}
}
}
}

}
}


GridDemo.mxml

//从dataProvider中将选中的项放入 selectedItems array
private function initSelectedItems():void{
for(var i:int=0; i <idata.length; i++){
if(idata[i].action.toString() == "true"){
this.selectedItems.push(idata[i])
}
}
}



源文件下载

写了一个简单的Demo,目的是帮助大家初步了解itemrender的使用,以及如何对多行数据同时操作。

Demo操作过程。

1. DataGrid为3列,第一列是check box.
2. 点击选中多行的check box.
3. 点击删除选中行,所有被选中的行的第三列的值改变为 Deleted.



GridDemo.mxml

public var selectedItems:Array = new Array(); //保存选中行

//将数组中的行的status 改为Delete,并且刷新Grid
private function removeSelectedItems():void{
for(var i:int = 0; i


<mx:DataGrid id="grid" dataProvider="{idata}">
<mx:columns>
<mx:DataGridColumn headerText="删除" dataField="action" itemRenderer="{new ClassFactory(iCheckBox)}" />
<mx:DataGridColumn headerText="姓名" dataField="name" />
<mx:DataGridColumn headerText="状态" dataField="status" />


</mx:columns>
</mx:DataGrid>


iCheckBox.as (ItemRender)

override public function set data(value:Object):void{
if(value.action.toString() == "true"){ //如果action为true,选中check box
this.selected = true;
Application.application.selectedItems.push(value) //将选中的数据保存起来
}else{
this.selected = false;
}
this.currentData = value; //保存整行的引用
}

//点击check box时,根据状况向selectedItems array中添加当前行的引用,或者从array中移除
private function changeHandle(e:Event):void{
var itemArray:Array = Application.application.selectedItems
this.currentData.action = this.selected.toString()
if(this.selected){
itemArray.push(this.currentData)
}else{
for(var i:int = 0; i<itemArray.length; i++){
if(itemArray[i] == this.currentData){
itemArray.splice(i,1)
}
}
}
}


GirdDemo.zip是上面的例子。
GridDemo1.zip是修改了checkBox为不在dataProvider中的column, tab切换可以Z字跳转。

Share object一般用来保存前来访问的用户的个人信息到用户的机器上。比如用户登陆过后保存他的名字,下次访问时自动显示,或者保存用户的个性化设置等等。 每个网站对应100K的默认大小,如果尝试写入超过100K的数据到客户端硬盘,flash player会有提示。这篇教程没有什么特别 之处,网上也有很多关于Share objecy的介绍,Share object很简单,我这里主要提供一个demo给大家。

Demo操作过程
1. 第一次运行后你可以看倒提示说您第一次来。
2. 填写用户名和爱好,点击记住我,你会看到你填写信息的显示。
3. 重新运行demo, 你的信息会被读出。
4. 点击忘记我,清空share object.
5. 填写新信息,点记住我,老信息会被替换。



下面是demo中使用的方法。

private var shData:SharedObject;

private function init():void{
shData = SharedObject.getLocal("demoData") //获取名称为demoData的 share object
this.showLocalData()
}
private function saveToLocal():void{
shData.data.userName = userName.text; //保存用户名
shData.data.like = like.text; //保存爱好
shData.flush(); //写入local
this.showLocalData();
}

private function showLocalData():void{
if(shData.data.userName == undefined || shData.data.userName == "" || shData.data.like ==""){
this.info.text = "您第一次来,请填写你的信息并点击记住我"
}else{
this.info.text = "您好,爱好" shData.data.like "的" shData.data.userName
}
}







源代码下载

现在说说Module,这篇教程代码不是最重要的,怎么样合理的使用Module以及注意的问题才是关键,所以建议大家注意下面红色语句。Module,可以将我们的项目按需划分为N个模块,在编译时将项目编译为主文件以及N个module的swf。Module基本上可以分为两种:

1. 完整的Module,可以被外部app所使用 -- 会将所有涉及到的引用编译到module中,主文件的体积得到缩减,但是Module本身的体积可能会很大,比如Module和主程序都应用了对象C,那对象C会被编译到主程序以及Module中,这样Module的体积就会很大。

2. 关联到主程序的Module -- 比如Module和主程序都应用了对象C,那对象C会只编译在主程序中,从而减小Module的体积。我今天主要讲这一种Module,也是我们最常用到的。



了解了Module的种类,再简单说说使用Module要注意的地方。

1. 绝对不能在Module以外的地方直接引用Module中的对象。这样Module会被编译到引用它的模块中去,如果从主程序中引用,那么Module就实际上没有效果了。

2. 既然不能引用,那么建议对Module对象的使用用接口实现。在Module中实现接口方法,在外部使用这个接口不会导致Module被误编译。

3. Module可以引用主程序中的东西,但是不要引用其他Module中的东西。


Module可以做什么呢?我主要将Module用于以下下两种情况

1. 缩减主程序的体积,点击Module功能块时加载Module.
2. 再入主程序后在用户感觉不到的情况下预加载剩下的Module.


现在讲正题,在demo中我这样表现Module的使用。(为了体现Module的意义,主程序生成的大小是原始flex大小248K, module内嵌了两张图片是674K)

1. 如何创建Module.
2. 主程序中点击按钮加载Module PictureWindow.
3. 加载完毕后将模块添加到Box中,并通过接口调用PictureWindow中的方法setSelectIndex()设置显示的图片。


1. 如何创建Module.

可以通过New --> MXML Module -->Optmize for applicaiton --> OK 或者修改任意的已经创建好的Container组建(比如Canvas, panel)标签为Module,再或者继承Module的As class。

之后确保“鼠标右键项目”--> Property --> Flex Module 中有这个Module,没有的话点Add --> 选择Module的mxml或as文件 -->Optmize for applicaiton -- > OK

2. 主程序中点击按钮加载模块PictureWindow.

这里我使用了ModuleManager来动态加载需要的Module。这比ModuleLoder要灵活的多。

private function loadModule():void{
m = ModuleManager.getModule("PictureWindow.swf"); //设置Module地址,地址是编译后swf在bin中的位置 //设置事件监听 m.addEventListener(ModuleEvent.READY,loadReady);
m.addEventListener(ModuleEvent.PROGRESS,loadReady);
m.addEventListener(ModuleEvent.ERROR,loadError); m.load(); //加载Module
}



3. 加载完毕后将模块添加到Box中,并通过接口调用PictureWindow中的方法setSelectIndex()设置显示的图片。

PictureWindow实现了PictureWindowInterface接口,其中暴露了setSelectIndex方法。再次强调不要直接使用Module对象,如果我们不注意写成
var window:PictureWindow = e.module.factory.create() as PictureWindow,那整个Module就前功尽弃了

//Module加载完成
private function loadReady(e:ModuleEvent):void{
//将Module对象转换为PictureWindowInterface
var window:PictureWindowInterface = e.module.factory.create() as PictureWindowInterface
this.box.addChild(window as DisplayObject);
window.setSelectIndex(1); //通过Interface调用Module中的方法
}



好了,Module的使用就写这么多,看到这里你应该也可以创建自己的Module了,对于ModuleManager和IModuleInfo 中详细的内容,大家可以查阅Flex帮助。



源文件下载

看了Moon和Grubby两场比赛。看的很爽,写点有意思的,用javascript可以获取本地ip,这个我就先不说了,我说说怎么能通过ip获取城市,能获取城市的话用处就比较大了,比如......

现在说正题。获取城市最大的问题是ip对应城市数据库,我们自己建这个数据库的话太麻烦。杂办呢,网上有很多可以根据ip查城市的网站,我们就用他们。首先找一个速度比较快的ip地址查询网站。我找的是 www.ipxxx.com(大家在源代码看吧,写出来成做广告了)。接下来确定三步

1. 看网站的结果是怎么得到的,经过观察发现上述网站是通过get查询,那就好办了。
2. 看看结果页面,因为网站不会给我们提供数据源的,所以我们要从结果页面找到我们想要的东西。上述网站的结果页面很干净,过滤很方便。
3. 确定它的首页没有版权信息说不准从外部直接访问某某页面。



OK,接下来就可以开始通过这个网站获取城市了,非常简单,不到10行代码。

1.创建一个到目标网站的HTTPService。


<mx:HTTPService method="GET" resultFormat="text" useProxy="false" id="ipGetter" url="http://www.XXXX.com/ips.asp" result="resultHandle(event)" fault="faultHandle(event)" />

2.处理结果页面。

private function resultHandle(e:ResultEvent):void{
var contnet:String = e.result.toString()
var begIndex:int = contnet.indexOf("省")
var endIndex:int = contnet.indexOf(" ",begIndex)
if(begIndex <0){
begIndex = contnet.indexOf("主数据:")
endIndex = contnet.indexOf(" ",begIndex)
contnet = contnet.substring(begIndex+4,endIndex)
}else{
contnet = contnet.substring(begIndex+1,endIndex)
}
this.result.text ="结果是:"+contnet;
}



OK,这样很节约我们的资源吧,不过这种方法有可能会涉及到版权问题,大家在尝试别的网站的时候要注意


不过因为受到安全沙箱的限制我们不能直接这么用,我们可以在我们的服务器端用相同的方法取得ip, flex直接获取数据就好







源文件下载

睡前没事突然想起有次在哪见过一个苹果样式的弹出窗口demo. 因为当时没有源码,也没有注意是怎么实现的。今天用一个简单的方式做一个试试。

我们知道Flex中内置了很多默认的Effect,其实只要我们稍加组合,可以实现很多很有意思的效果,这次的下滑菜单就用了其中两个效果,wipeUp和Move,然后用Parallel把他们组合起来用。我现在来详解。

Demo中我创建了一个KAlert类,用它弹出的信息框就是下滑样式了,这其中我只添加了一个静态的show()方法,重点就是我在其中调用的 setAppleStyle(alert)和setOpenEffect(alert)。
分别用来设置样式和设置弹出动画。

public static function show(text:String,modal:Boolean = false):void{
var alert:Alert = new Alert();

setAppleStyle(alert);
setOpenEffect(alert);



alert.text = text;
var parent:Sprite = Sprite(Application.application);
PopUpManager.addPopUp(alert, parent, modal);
alert.x = Application.application.width/2-100;
alert.setActualSize(alert.getExplicitOrMeasuredWidth(), alert.getExplicitOrMeasuredHeight());

}




样式设置就是让弹出框看上去更像mac的样式。我们主要来看下面的setOpenEffect(alert);,看其中的注释既可,demo就靠这个方法实现下滑弹出窗口。



//设置弹出动画
private static function setOpenEffect(alert:Alert):void{
var wipeUp:WipeUp = new WipeUp(); //Wipe效果,方向向上
var move:Move = new Move(); //移定效果
var parallel:Parallel = new Parallel(); //加入其中的效果会被并发执行
parallel.addChild(move);
parallel.addChild(wipeUp);

//设置弹出框在正中央
move.xFrom = Application.application.width/2-100;
move.xTo = Application.application.width/2-100;

//弹出时从-100移动到100,同时wipeUp
move.yFrom = -100;
move.yTo = -1;
//动画执行时间为300毫秒
wipeUp.duration = 300;
move.duration=300;
//在当前控件被加入舞台时执行
alert.setStyle("addedEffect",parallel)
}



源文件下载