Posts tagged ‘AS3’

betterbeansbinding

第一次用java swing写了套桌面程序,来自动化一些工作上的事情。有人问我为什么不用AIR,毕竟是比较熟悉的flash开发模式,我的理由是AS相关的文档处理类库不够丰富,可能这也是AIR的一个不太好的地方。

熟悉AIR和swing的人应该知道,AS这类动态语言在UI编程的效率方面比java好N多,比如bind非常实用和简便。可是java swing借助一些丰富的类库也能实现类似功能,比如这次要讲的betterbeansbinding,可以实现JList,JTable等swing组件和java beans的绑定,单向或双向的都行。

举例一段代码

jtb = SwingBindings.createJTableBinding(UpdateStrategy.READ_WRITE, vec, wage_list);

BeanProperty<SJPerson, ?> sjp_name = BeanProperty.create("personName");
BeanProperty<SJPerson, ?> sjp_wage1 = BeanProperty.create("wage1");
BeanProperty<SJPerson, ?> sjp_wage2 = BeanProperty.create("wage2");

jtb.addColumnBinding(sjp_name).setColumnName("姓名").setEditable(false);
jtb.addColumnBinding(sjp_wage1).setColumnName("底薪").setColumnClass(Float.class);
jtb.addColumnBinding(sjp_wage2).setColumnName("技术津贴").setColumnClass(Float.class);
jtb.bind();

它的作用是将

  • Vector<SJPerson> vec
  • JTable wage_list

双向绑定,vec是一组beans,在JTable里显示一个row。

更多信息可搜索或者Google code

Degrafa的Bezier源码浅析(一)

先解释下Bezier曲线:

http://en.wikipedia.org/wiki/Bézier_curve

Degrafa是一套很强大的图形算法类库,并且开源,对Bezier曲线的绘制提供了很好的支持。说起Bezier曲线,其实就是Photoshop和flash里常用的钢笔工具啦,主要就是用来绘画路径或者绘画不规则图形。前者的应用领域非常广泛,比如使图形沿着路径运动,或者按路径不规则排列图形(具体一例就是扭曲的美术字布局)。这些都会用到Bezier曲线。

Degrafa提供的tutorial不是很完备,很多时候为了扩展应用不得不看源码。我这里只就Bezier相关的处理过程分析一下。

Degrafa下面的类非常之多,跟Bezier相关的主要有:

  • com.degrafa.geometry.splines.BezierSpline
  • com.degrafa.geometry.CubicBezier

两者都继承自com.degrafa.geometry.Geometry。很多图形库都有着类似的继承体系,Geometry作为一个基础的图形类,下面还有很多经过定义的不同种类的图形。

CubicBezier看字面意思,是一个三次贝塞尔曲线,事实上确实如此,它有四个点(start,end,2 control points)控制,其实也就是wiki上的那个图形

而BezierSpline跟真正的钢笔工具非常像,它由一系列点和control points来控制一整条曲线。这里最好看它的注释:

The BezierSpline can be used for drawing of a smooth curve through
multiple points, with some shape control over the curve via a tension
parameter. It may also be used for general path animation with tension
control, optional closed-path control, and velocity control
(arc-length parameterization).

乍看一下,它好像就是若干个CubicBezier的组合,其实关系不是很近。BezierSpline内部确实用到了CubicBezier对象(源码的第一行private var _bezier:Array),它是用来管理三次bezier曲线段的(BezierSpline包括很多的段)。

在使用BezierSpline曲线之前,需要提供一系列的点(钢笔工具的点类似)。使用到的property包括:

  • points
  • data

两者实质相同形式不同而已。

绘画曲线都使用draw方法,这个方法是由Geometry定义,并由它的子类重写的(典型的继承体系)。BezierSpline和CubicBezier都有类似的draw定义方式:

/**
* Begins the draw phase for geometry objects. All geometry objects
* override this to do their specific rendering.
*
* @param graphics The current context to draw to.
* @param rc A Rectangle object used for fill bounds.
**/
override public function draw(graphics:Graphics,rc:Rectangle):void{	

	//re init if required
 	if (invalidated) preDraw(); 

	//init the layout in this case done after predraw.
	if (_layoutConstraint) calculateLayout();

	super.draw(graphics, (rc)? rc:bounds);

}

preDraw在这个体系里非常重要,是用来在绘画之前计算各个点的位置,就像我们使用graphic那样定义好MoveTo和lineTo,所不同的这里会用到很多算法。与draw一样,preDraw在Geometry里定义在子类里重写,每个子类都有不同的方法。比如BezierSpline:

/**
* @inheritDoc
**/
   override public function preDraw():void{
   	if( invalidated ){

        	if(!points.length){return;}

        	_assignControlPoints();

        	//add a move to for the first item.
        	commandStack.length=0;

	//add a MoveTo at the start of the commandStack rendering chain
	commandStack.addMoveTo(points[0].x,points[0].y);

	var cubic:CubicBezier;

        //todo not sure were this extra one is coming from yet.
        //re:: - 1 on the count
        for( var i:uint=0; i<_bezier.length-1; ++i ){

        	cubic = _bezier[i];

        	commandStack.addCubicBezierTo(cubic.x0,cubic.y0,cubic.cx,
       	cubic.cy,cubic.cx1,cubic.cy1,cubic.x1,cubic.y1,1);

  	}

     		invalidated = false;
}
	    }

挖到这里,基本上快看到bezier算法在Degrafa里怎么实现的了,关键就是这个commandStack,字面意思:命令退栈,命令集合。

避免try catch,提升Flash性能

在一些实时性能要求比较高的应用中,如3D,try…catch处理错误会严重的影响性能和执行效率,应该尽量使用判断语句来代替。为此我有个实验。

if (theOldIndex >= 0 && theOldIndex < this._cache_pop_yw_zws.length)
{
	_gra.lineStyle(1, 0xffffff);
	_gra.beginFill(colorFront);
	_gra.drawCircle(theCachedArr[theOldIndex].x, theCachedArr[theOldIndex].y, 6);
	_gra.endFill();
}

这里主要是为了不让theOldIndex溢出造成错误,如果每次ENTER_FRAME都是用try catch来判断,大概用getTime测试的执行时间会大上一倍多,太杯具了。因此尽量要预见可能出现的问题,有时情愿写code麻烦点。我想这也是c可以长盛不衰的原因吧。

AIR开源项目教程系列(三)测试与涂鸦并举

终于到了编码阶段,写code大家都熟悉不细讲,有兴趣可以上svn下载jonFTP的代码,带有一些注释应该容易看懂。我这里要谈到的不是如何写code,而是写code的过程。在此之前,有些知识需要提前弄明白(每个AIR项目都有不同的知识基础,要在编码之前学习,边学边写不是啥好习惯),如FTP协议,命令等。

就我个人来讲,测试驱动和设计图是两条主轴,两条故事线。而设计图应该在测试驱动之前。

设计涂鸦

是UML吗,不一定,设计图主要是为了理清思路,知道哪里抽象,哪里改写。基于这个目的,是不是UML,要不要遵守UML语法并不是问题核心,关键自己要有数,看不懂不行。而设计图也应该先从逻辑层开始,下面是我涂鸦出来的JonFTP的架构:

GLFTPClient

这里最重要的就是GLFTPClient,GLProcess和GLGroup:

  • GLFTPClient代表一个session,管理socket,处理发送和接收字节。
  • GLProcess代表一个处理单元(将FTP命令分类管理,组织成不同的逻辑单元),是个抽象类,子类都是具体的逻辑单元,比如GLList,就是取得当前远程路径下的文件信息。GLProcess向GLFTPClient发送指令,并分析GLFTPClient获得的字节。
  • GLGroup,一个特殊的GLProcess,管理逻辑组合,比如批量删除,上传和下载。

这只是一个大致的结构(实际上我都是在草稿纸上画的),反正就一个意思。设计图帮助你理清逻辑层的架构,时时刻刻保证路线正确。

差点忘了,时刻重构来修改设计图。

测试驱动

我不是要讲TDD怎么怎么好,这里只是要提醒,时刻准备好测试你的代码,如果你先有算法,那么就测试算法;如果你有一些单元功能,就准备一个简单的UI来展示一下。反正就是说,时时刻刻要测试你的工作。这样可以尽早发现问题。

山寨版AS3事件冒泡机制的实现

AS3实现了事件传递,分成三段Capture,target和bubble,其中bubble就是向树根传递。这个机制非常之经典和好用,可惜它只存在在基于DisplayObject的对象树中,其它时候只能把事件老老实实的从一个对象直接传递到另一个对象。

这次我来玩个有趣的。仿造AS3内置的机制来制作一个更加通用的冒泡框架,相信可以用很多用处(至少已经用在我的项目上了)。自己实现事件传递,让它支持任何事件任何数据结构(无论队列,还是树),甚至可以传递任何对象(而不仅限于事件对象)。其实bubble的过程不难,就是把一个对象(特例就是事件对象)在一个链表里自动传递,不断的向上抛,过程跟AS3的事件机制别无二致。为了能扩展,我准备了接口和初步的实现类:IBubbleDispatcher和CBubbleDispatcher。跟IEventDIspatcher和EventDispatcher一样吧。来看看代码。

package org.gl.geom.abstract 
{
	import flash.events.Event;
 
	/**
	 * ...
	 * @author Jonson
	 */
	public interface IBubbleDispatcher 
	{
		/**
		 * add to bubble tree
		 * 
		 * @param	child
		 * @param	father
		 */
		function registerAsMyChild(child:IBubbleDispatcher):void;
 
		/**
		 * dispatch event, then bubble
		 * 
		 * @param	event
		 */
		function bubble(event:Event):void;
 
		/**
		 * it's better to be override in subclass
		 * 
		 * @param	event
		 * @return	true means continue bubble
		 */
		function myBubbleEventHandler(event:Event):Boolean
	}
 
}
package org.gl.geom.abstract 
{
	import flash.events.Event;
 
	/**
	 * ...
	 * @author Jonson
	 */
	public class BubbleDispatcher implements IBubbleDispatcher
	{
		//////////////////////////////////////////////////
		// render event bubble
 
		private var __my_parent__:IBubbleDispatcher;
 
		/**
		 * add to bubble tree
		 * 
		 * @param	child
		 * @param	father
		 */
		public function registerAsMyChild(child:IBubbleDispatcher):void
		{
			if (child is BubbleDispatcher)
			{
				(child as BubbleDispatcher).__my_parent__ = this;
			}
		}
 
		/**
		 * dispatch event, then bubble
		 * 
		 * @param	event
		 */
		public function bubble(event:Event):void
		{
			if (__my_parent__)
			{
				var neve:Event = event.clone();
				if (__my_parent__.myBubbleEventHandler(neve))
				{
					__my_parent__.bubble(neve);
				}
			}
		}
 
		/**
		 * it's better to be override in subclass
		 * 
		 * @param	event
		 * @return	true means continue bubble
		 */
		public function myBubbleEventHandler(event:Event):Boolean
		{
			//it's better to be override in subclass
			return true;
		}
	}
}

为了便于理解,我这里与AS3内置的EventDispatcher做些比较。

  • 把CBubbleDispatcher做父类(和EventDispatcher一样)。
  • 使用registerAsMyChild注册叶子对象(类似于addChild和addEventListener的组合,先把叶子对象作为当前对象的child,然后注册监听),这样的话,叶子抛出的事件对象就能被父亲对象处理了,或者再向上抛。
  • 使用bubble发出事件(相当于dispatchEvent)。
  • 子类重写myBubbleEventHandler来处理事件,返回true说明继续向上传递(类似于stopPropagation功能),如果不需要处理就不必重写。这样设计的好处是,不需要根据不同的Event type而写上很多很多的addEventListener。

补个小例子

class parent1Class extends CBubbleDispatcher
{
      public function parent1Class(){this.registerAsMyChild(_child);}
      private var _child:parent2Class = new parent2Class();
}
class parent2Class extends CBubbleDispatcher
{
      public function parent2Class(){this.registerAsMyChild(_child);}
      private var _child:childClass = new childClass();
}
class childClass extends CBubbleDispatcher
{
      public function try():void{this.bubble(new Event("UP"))};
}

Flex源码学习之mx.utils.*

我承认这个标题有点标题党的味道。其实我的意思是想说,flex framework里有些code并不是依赖于framework的,怎么说呢,它们是纯算法,是数据处理,而并没有用到其它的flex framework class已经一些,比如metadata特性(有也可以改掉)。比如下面要提到的mx.utils.ColorUtil,还有StringUtil。于是我们就可以把它们提炼出来,用在普通AS3/Flash project当中,而不需要依赖,导入flex framework。

flex sdk已经开源了,进入sdk folder查找ColorUtil。来看看它处理brightness的算法

/**
 *  Performs a scaled brightness adjustment of an RGB color.
 *
 *  @param rgb Original RGB color.
 *
 *  @param brite The percentage to brighten or darken the original color.
 *  If positive, the original color is brightened toward white
 *  by this percentage. If negative, it is darkened toward black
 *  by this percentage.
 *  The range for this parameter is -100 to 100;
 *  -100 produces black while 100 produces white.
 *  If this parameter is 0, the RGB color returned
 *  is the same as the original color.
 *
 *  @return New RGB color.
 */
public static function adjustBrightness(rgb:uint, brite:Number):uint
{
	var r:Number;
	var g:Number;
	var b:Number;
 
	if (brite == 0)
		return rgb;
 
	if (brite < 0)
	{
		brite = (100 + brite) / 100;
		r = ((rgb >> 16) & 0xFF) * brite;
		g = ((rgb >> 8) & 0xFF) * brite;
		b = (rgb & 0xFF) * brite;
	}
	else // bright > 0
	{
		brite /= 100;
		r = ((rgb >> 16) & 0xFF);
		g = ((rgb >> 8) & 0xFF);
		b = (rgb & 0xFF);
 
		r += ((0xFF - r) * brite);
		g += ((0xFF - g) * brite);
		b += ((0xFF - b) * brite);
 
		r = Math.min(r, 255);
		g = Math.min(g, 255);
		b = Math.min(b, 255);
	}
 
	return (r << 16) | (g << 8) | b;
}

是不是很优雅。Adobe的精英工程师创造出来的,效率及简介性肯定可以保证,况且开源了,为啥不好好利用呢。flex code里有很多好货可以发掘。

AIR开源项目教程系列(二)细化结构设计

假设之前已经有一张列表,描述操作流程(也就是初定下的需求)。下面就可以开始设计程序结构了。我觉得,拉起来就写code并不是多好的习惯,至少脑子里也该有个结构概念吧。

还是MVC

MVC是个永恒经典的结构模型,不光可以脱离UI的束缚,而且可以让人形成一种习惯,就是,没了UI也叫程序,让逻辑决定程序的走向。试想下,如果AIR项目没有UI那算啥。设计就要先从没有UI开始。这一点,flex和flash做得不是太好,因为程序的开头总是sprite或mxml。

所以说,细化程序结构的第一步就是拆分逻辑层和视图层。拿JonFTP为例,现在抛弃UI的概念,试想下一个FTP流程该什么样子。登录,上传,关闭。一个主要的流程就是如此。

workflow1

workflow1

若干流程的集合就是逻辑层,不需要想的太复杂,比如用户该怎么选怎么点,这是视图层该干的事情。

怎么把若干流程揉合在一起

我以前总是到最后才考虑怎么把各个功能综合起来,其实它应该在考虑细节之前考虑。比如上面讲的,登录,上传,关闭是一条路,登录,上传,下载,关闭也是条路,中间的操作可以任意组合而不应该含有冲突。也就是说,无论怎么变化,有几点是肯定的:

  • 登录,关闭一定是一条流程的首尾
  • 中间的操作可以任意变化,且不能相互冲突
  • 流程之前不能有联系(新的session需要用户身份)

于是,我就设计了这么个结构:

  • 准备一个“工厂”,产生,托管和消灭一条流程(Session)
  • 流程以登录操作为开始,以关闭操作为结束
  • 任何操作方式(包括登录和关闭)设计为独立的过程(process),过程之间可能要有联系。不要忘了,过程组合也是过程的一种。

初期我是这么考虑的,到了后来还是出现了一些变化,这是后话。

转换到计算机语言

有了上面的“大白话”结构,就可以用计算机语言(AS3)来描述了。相应的就是:

  • 准备一个全局的FTPClient类,代表一个session,实例由工厂方法或者工厂类产生。
  • 准备一个树状的继承结构,顶层为CProcess,代表一个过程(向FTP服务器请求并获得数据,解析),子类包括一个CGroup,可以add若干个CProcess,它代表一个过程组合(比如批量删除)。
  • FTPClient接受CProcess作为输入,执行并处理数据,CProcess以事件方式通知UI执行成功与否。

初期的设计就是如此,不过随着开发的进展,肯定会有修改甚至是推翻。不过都是正常的。这里总结下我的设计步骤

  1. 拆分逻辑层和视图层
  2. 先设计逻辑层
  3. 逻辑层以流程(session)为主导,不可依赖用户操作
  4. 以书面化形式描述各种流程,并寻找相同点和不同点
  5. 转换用计算机语言描述,并形成一个大致的架构
  6. 开发过程中不断修改

FlexDbg试用 — debug plugin of FlashDevelop

FlashDevelop这个IDE本身是不带断点调试的,但是可以通过插件的方式来支持。FlexDbg就是这么一款好用的插件(下载见这里)。使用步骤如下:

  1. 下载(废话)最新版zip包
  2. 打开FlashDevelop的安装文件夹,进入Plugin文件夹,将下载的zip包解压到这里(几个dll文件)
  3. 打开或重启FlashDevelop,应该发现menu上多了debug选项
  4. debug的方式几乎和别的IDE没啥不同,不过注意了,不再是点“Test Movie” 这个蓝色小箭头来启动debug,而是menu->debug->Start,要用菜单栏的。

我的感觉,FlexDbg的调试跟visual studio感觉一样。开发AS没有断点调试功能怎么行,快试试看吧。

FDT3,FlashDevelop,FlexBuilder的比较

昨天把FDT3给安上去了,用了好长时间。至此把FDT,FlashDevelop,Flex builder三类主流的IDE都用了遍,来谈谈自己的感受。

FDT3

应该说这是个相当不错的IDE,基于eclipse,可惜挺贵。进第一家公司的时候就被迫用它来开发AS2,应该说,他的代码提示和代码完成相当的好,对语法检查很严格(包括提示你打;一行指令的结束符)。FDT3这个版本包罗万象,可以开发flex和AIR,还自带sdk。缺点就是太吃内存了,让人有点受不了。

FlashDevelop

快速轻便,适合纯写代码,可能是托了visual studio shell的福吧,代码提示跟visual studio的感觉如出一辙,只是在某些方面差了些,比如继承关系中不会提示有些方法没有implement或override,可能直到编译的时候才发现,这点不如FDT。FlashDevelop不自带SDK,需要自行下载。这我觉得是个好处,可以自己控制。总之非常的轻巧功能不弱。

Flex builder

这里只谈3因为还没用过4。这个IDE感觉除了UI可预览,其它的都不如上面两款。基于eclipse的,块头自然不必说,吃掉很多资源(而且是越来越能吃了)。代码提示一般性。另外我感觉,如果用熟的话,我基本不开UI预览。flex有很强大的layout组件,靠这些组件基本上可以想象的到UI是什么样子。如果只是开发AS project,还是推荐上面两款。

递归调用处理连续的异步操作

今天来总结一种个人常用的处理方式。

案例:

在循环语句中处理一些操作,很可惜,操作都是异步的(比如向服务器访问,必须要用事件等待回应)。而且,必须要求是上一步完成后才能进行下一步。我们知道,AS是不能阻塞线程的。所以用循环语句不行。

我的办法:

准备一个方法,如function(index:int, maxlength:int, …)。在这个function里执行异步操作,等拿到结果后再调用function(index+1, maxlength),形成递归调用,当然执行异步操作之前必须检测index<=maxlength,要让循环结束嘛。注意到…了吗,可能你的操作还需要一些别的变量,如果你需要的变量特别多的话,我还是建议用内嵌函数,用code说话吧:

var func:Function = function(index:int, maxlength:int):void
      {
            ...
      }
 
//start
func(0, maxlength);

它就能利用内嵌函数的特性,来直接取用你的类变量或者函数内变量了(局部变量)。AS3当然会有这种特性,动态语言都有类似特性。