胡矿的Blog

August 30, 2007

Filed under: AsWing 教程 — thiswind @ 1:38 pm

AsWing入门教程 3.1 模型-视图-控制器设计模式

作者:胡矿

著作权所有,请勿转载

www.flashseer.org

Google Doc

http://docs.google.com/Doc?id=dnp8gdz_33f8dk27

模型-视图-控制器设计模式又被称为MVC。这个模式是将数据模型与显示界面分离,通过一个控制器来分别与数据模型和界面沟通和进行控制。 如图3.1-1.

(图3.2-1)

由于ActionScript3.0是事件驱动的强交互性语言,所以模型-视图-控制器设计模式在ActionScript3.0当中也会与在其他语言中的定义有所不同。

  • 数据模型是一个事件源(EventDispatcher)
  • 视图是一个事件源(EventDispatcher)
  • 控制器里面你有两个通道

1.一个通道监听视图的变化,并且把变化写入到模型

2.一个通道监听模型的变化,并且把变化输出到视图

如图3.2-2。

(图3.2-2)
当视图(View)被触动(比如鼠标点击或者键盘输入),视图将发出事件,这时候视图-模型通道(ViewToModelChannel)收到事件并且更新模型(Model)的内容。如图3.2-3所示。
(图3.2-3)
当模型(Model)发生改变,模型将发出事件,这时候模型-视图通道(ModelToViewChannel)收到事件并且更新视图(View)的显示。如图3.2-4所示。
(图3.2-4)

下面是这个模式的代码表述:

Model

package
{
import flash.events.EventDispatcher;

public class Model extends EventDispatcher
{
}
}

View

package
{
import flash.events.EventDispatcher;

public class View extends EventDispatcher
{
}
}

Controller

package
{
public class Controller
{
private var v2mChannel:Channel;
private var m2vChannel:Channel;

public function Controller(view:View, model:Model) {
this.v2mChannel = new ViewToModelChannel(view, model);
this.m2vChannel = new ModelToViewChannel(view, model);
}
}
}

class Channel {
protected var view:View;
protected var model:Model;

public function Channel(view:View, model:Model) {
this.view = view;
this.model = model;
}
}

class ViewToModelChannel extends Channel {

public function ViewToModelChannel(view:View, model:Model) {
super(view, model);

//监听来自视图的事件
this.view.addEventListener(……..
}
}

class ModelToViewChannel extends Channel {

public function ModelToViewChannel(view:View, model:Model) {
super(view, model);

//监听来自模型的事件
this.model.addEventListener(…….
}
}

下面,我们用一个简单的例子来介绍这个设计模式如何使用。在后面的章节当中,我们会大量使用这里所介绍的模型-视图-控制器设计模式。

如图3.2-5和3.2-6,这个程序有一个输出框架和一个输入框架。在输入框架增加或者减少模型的顶点,输出框架根据模型当中的顶点数绘制正多边形。

(图3.2-5)

(图3.2-6)

在这个例子当中,模型定义了一个vectorChanged事件(VECTOR_CHANGED)和一个正整数(vectors)。定义了两个公开的方法来操纵这个正整数(getVectors、setVectors)。如图3.2-7。

(图3.2-7)

模型对于外面发生的事情一无所知,它只知道在vectors改变的时候发布vectorChanged事件(VECTOR_CHANGED)。


笔记:

一个对象是由数据和方法所构成的,数据定义的是对象的状态,所以有的时候,数据又被叫做属性,比如例子当中的Model,数据域vectors表示这个Model目前是处在3个顶点的状态,或者是4个顶点的状态。

我们要强调的是对象当中的方法。对象当中的方法是对本对象自身的数据的操作。所以方法(Method)有时候又被叫做操作(Operation)。

记住这句话:数据(属性)是对象的状态,方法是对数据(属性)的操作。



笔记:

面向对象的一个基本概念是服务(Service)。

当一个对象要访问另一个对象的时候(即改变或者读取另一个对象的状态),它只能通过那个对象所提供的服务来进行操作,在对象这个层面,服务就是方法(Method)。而一个对象要让别的对象可以操作自己的状态,就要定义操作自己数据的方法。

注意:一个对象的方法只应该操作自己的数据,而不应该去操作其他对象的对象的数据。要操作别的对象,应该通过别的对象所提供的服务(Method)


视图是一个有两个框架(JFrame)的界面,它只提供了一个公开的方法update,这个方法接受一个正整数作为参数,并且将根据这个正整数来绘制一个正多边形。如图3.2-8。

(图3.2-8)

视图对于模型同样一无所知。它只知道代表顶点数目的一个正整数,而对这个数字从何而来,代表什么含义一无所知。在“添加顶点”(Add Vector)和“移除顶点”(Remove Vector)按钮被按下的时候,视图将发布addVector事件(ADD_VECTOR)和removeVector事件(REMOVE_VECTOR)。

控制器负责间监听分别来自模型和视图的事件。如图3.2-9。

(图3.2-9)

控制器有两个通道(Channel),一个负责监听和处理来自模型的事件(ModelToViewChannel),另一个负责监听和处理来自视图的事件(ViewToModelChannel)。控制器对于视图将如何显示以及模型将如何保存数据一无所知,它只知道在收到特定的事件的时候对数据进行逻辑处理,并且在需要的时候通过模型或者视图所提供的方法改变他们的状态。

我们看到,模型-视图-控制器设计模式当中,每一个层面都只负责自己层面的处理逻辑,对于其他层面是如何进行处理的一无所知。不同层面之间只是通过一些特定的接口(Method)进行通信。并且在这个设计模式当中视图和模型是被隔开的,他们之间不能直接访问,而是要通过一个控制器来来处理模型和视图之间的逻辑。这样的好处在于

  • 当我们需要对某一层进行修改的时候,只要保证接口的定义不发生变化,这个修改就不会影响到其他的层面。
  • 界面显示部分的逻辑、数据的处理逻辑、数据的存储逻辑被彻底分离。每一个层面都只关注自己层面的逻辑而不用关心别的层面具体是如何进行处理的。


注意!

这里提到的接口是广义的接口,而不是指用关键字interface所定义的接口类。

接口是连接两个模块的规范(在面向对象当中,最基本的模块就是对象)。在对象这个层面,接口就是一个方法定义,这个定义除了方法的返回类型和参数以外,还包括方法的前置条件(Pre-condition)和后置条件(Post-condition)。

接口方法:提供给其它对象使用,用来操纵自身对象的方法。

前置条件:要让这个方法得到正确的结果,需要具备一些什么样的先决条件。(比如参数不能为null)

后置条件:这个方法结束之后将会有怎样的后果。

前置条件和后置条件并不是ActionScript3.0用语法约束硬性规定的,它可以是一种口头约定,或者是被写在方法的注释当中或者文档当中的描述性文本。并不是所有的方法都需要注明前置条件和后置条件,但是在一些关键的接口方法的定义当中,应该写明前置条件和后置条件。

关键的接口方法的前置条件和后置条件往往被忽略,这是造成将来维护困难的一个重要原因。


例3.2-1是完整的程序代码。

例3.2-1

Model

package
{
import flash.events.EventDispatcher;
import flash.events.Event;

[Event (name=”vectorChanged”, type=”flash.events.Event”)]

public class Model extends EventDispatcher
{
public static const VECTOR_CHANGED:String = “vector changed”;

private var vectors:uint = 1; //默认是1个顶点

/**
* Post-condition:返回vector的数目
*/

public function getVectors() :uint {
return this.vectors;
}

/**
* pre-condition: n 不能为负数
*
* post-condition: vector 的数目将会被设为 n
*/

public function setVectors(n:uint) :void {
this.vectors = Math.max(n, 1); //确保至少有一个顶点
//发布更新事件
var event:Event = new Event(Model.VECTOR_CHANGED);
this.dispatchEvent(event);
}
}
}

View

package
{
import flash.display.DisplayObjectContainer;
import flash.display.Shape;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.MouseEvent;

import org.aswing.ASColor;
import org.aswing.AsWingManager;
import org.aswing.EmptyLayout;
import org.aswing.FlowLayout;
import org.aswing.JButton;
import org.aswing.JFrame;
import org.aswing.JPanel;
import org.aswing.geom.IntPoint;
import org.aswing.graphics.Graphics2D;
import org.aswing.graphics.Pen;

[Event (name=”addVector”, type=”flash.events.Event”)]
[Event (name=”removeVector”, type=”flash.events.Event”)]
public class View extends EventDispatcher
{
public static const ADD_VECTOR:String = “add vector”;
public static const REMOVE_VECTOR:String = “remove vector”;

private var owner:DisplayObjectContainer;
private var canvas:JPanel;
private var shape:Shape;

/**
* 构造函数
* pre-condition: owner 对象不能为空,而且必须已经被添加到Stage的显示列表当中
*
* post-condition:将构造视图的界面
*/

public function View(owner:DisplayObjectContainer) {
this.owner = owner;

AsWingManager.initAsStandard(this.owner);

//创建显示多边形的框架
var outputFrame:JFrame = new JFrame(this.owner, “Output”);

this.canvas = new JPanel(new EmptyLayout()); //用空布局
this.canvas.setSizeWH(400, 400);

this.shape = new Shape();
//将Shape的坐标原点移动到JPanel的中央
shape.x = 200;
shape.y = 200;
this.canvas.addChild(this.shape);

outputFrame.getContentPane().append(this.canvas);
outputFrame.pack(); //根据内容的尺寸自动计算框架的尺寸

//创建输入用的框架
var inputFrame:JFrame = new JFrame(this.owner, “Input”);

inputFrame.setLocationXY(450, 0);
inputFrame.getContentPane().setLayout(new FlowLayout());

var addVectorBtn:JButton = new JButton(”Add a Vector”);
addVectorBtn.addEventListener(MouseEvent.CLICK, this.__onAddVector);
inputFrame.getContentPane().append(addVectorBtn);

var removeVectorBtn:JButton = new JButton(”Remove a Vector”);
removeVectorBtn.addEventListener(MouseEvent.CLICK, this.__onRemoveVector);
inputFrame.getContentPane().append(removeVectorBtn);

inputFrame.pack(); //根据内容的尺寸自动计算输入框架的尺寸

outputFrame.show();
inputFrame.show();
}

private function __onAddVector(e:Event) :void {
var event:Event = new Event(View.ADD_VECTOR);
this.dispatchEvent(event);
}
private function __onRemoveVector(e:Event) :void {
var event:Event = new Event(View.REMOVE_VECTOR);
this.dispatchEvent(event);
}

/**
* pre-condition: n 是一个uint类型的正整数,如果传进来的参数是一个负数,那么这个负数经过uint类型
* 转换之后将会变成一个巨大的正数,这将会造成严重的运行错误。
*
* post-condition: 将绘制一个正n边形
*/

public function update(n:uint) :void {
var g:Graphics2D = new Graphics2D(shape.graphics);
var pen:Pen = new Pen(new ASColor(0×666666), 3);

g.clear();
g.drawPolygon(pen, this.calculateVectors(n));
g.endDraw();
}
private function calculateVectors(n:uint) :Array {
var theta:Number = 2*Math.PI/n;

var radius:uint = 150; //半径150像素

var array:Array = new Array(n); //初始化一个长度为n的数组
var point:IntPoint;
var x:int;
var y:int;
var angle:Number;
for (var i:uint=0; i<n; i++) {
angle = i*theta;

angle -= Math.PI; //将角度调整到 [-PI, PI] 区间

x = (radius*Math.cos(angle));
y = (radius*Math.sin(angle));
point = new IntPoint(x, y);
array[i] = point;
}
return array;
}
}
}

Controller

package
{
public class Controller
{
private var v2mChannel:Channel;
private var m2vChannel:Channel;

/**
* Pre-condition:view 不为null;model不为null
*
* Post-condition:将开始控制 view 和 model 之间的数据
*/

public function Controller(view:View, model:Model) {
this.v2mChannel = new ViewToModelChannel(view, model);
this.m2vChannel = new ModelToViewChannel(view, model);
}
}
}


//Inner classes


import flash.events.Event;

class Channel {
protected var view:View;
protected var model:Model;

public function Channel(view:View, model:Model) {
this.view = view;
this.model = model;
}
}

class ViewToModelChannel extends Channel {

public function ViewToModelChannel(view:View, model:Model) {
super(view, model);

//监听来自视图的事件
this.view.addEventListener(View.ADD_VECTOR, this.__onAddVector);
this.view.addEventListener(View.REMOVE_VECTOR, this.__onRevoveVector);
}

private function __onAddVector(e:Event) :void {
this.model.setVectors(this.model.getVectors()+1);
}
private function __onRevoveVector(e:Event) :void {
this.model.setVectors(this.model.getVectors()-1);
}
}

class ModelToViewChannel extends Channel {

public function ModelToViewChannel(view:View, model:Model) {
super(view, model);

//监听来自模型的事件
this.model.addEventListener(Model.VECTOR_CHANGED, this.__onVectorChanged);
}

private function __onVectorChanged(e:Event) :void {
this.view.update(this.model.getVectors());
}
}

API:org.aswing.JFrame

  • setLocationXY(x:int, y:int):void

设置框架的位置,x,y参数分别表示框架左上角定点的坐标

  • pack():void

根据所包含组件的默认大小来自动调整框架的尺寸。

API:flash.events.EventDispatcher

  • dispatchEvent(event:Event):Boolean

发布事件。所发布事件的event.target指向发布事件的对象

API:org.aswing.graphics.Graphics2D

  • drawPolygon(pen:Pen, points:Array):void

绘制多边形。多边形是封闭的,第一个顶点和最后一个顶点之间有连线。

API:Math

  • Math.PI

圆周率

  • Math.cos(angle:Number):Number

计算余弦。angle的单位是弧度。angle位于[-PI, PI]区间

  • Math.sin(angle:Number):Number

计算正弦。angle的单位是弧度。angle位于[-PI, PI]区间

  • Math.max(value1:Number, value2:Number):Number

返回两个数当中较大的数

标记:Event

语法:[Event (name=”eventName”, type=”package.eventType“)]

这个标记在MXML当中很有用,对于普通的ActionScript项目来说,这个标记可以在FlexBuilder2(或者FlexBuilder3)当中触发代码提示。

一般来说,如果某一个类要发布事件,应该用Event标记指出它将会发布哪些事件。

  • eventName

其所关联的类当中的属性为 EVENT_NAME,即,在标记当中,事件名称的首字母小写,之后每一个单词的第一个字母大写来区分其中的单词。而其所对应的类当中属性的名称全部用大写,单词之间用下划线分开。

  • type

这个事件的类型,由于例子当中用的就是Event,所以这里写的就是flash.events.Event。当然,它也可以是flash.event.MouseEvent或者是你自己定义的事件类型。

Filed under: AsWing 教程 — thiswind @ 1:37 pm

AsWing入门教程 第三章 基本AsWing组件

作者:胡矿

著作权所有,请勿转载

www.flashseer.org

Google Doc

http://docs.google.com/Doc?id=dnp8gdz_34dwn4ws

从本章开始将要介绍AsWing的组件以及相关的编程技巧。

Filed under: AsWing 教程 — thiswind @ 1:35 pm

AsWing入门教程 2.2 鼠标事件

作者:胡矿

著作权所有,请勿转载

www.flashseer.org

Google Doc

http://docs.google.com/Doc?id=dnp8gdz_30dmhcpf

在上一节当中,我们介绍了普通的事件处理。在这一节我们要介绍如何捕捉鼠标事件、键盘事件和焦点事件。

2.1.1鼠标事件。

在AsWing当中的组件都是Sprite的子类,AsWing组件又可以当作Sprite使用。

图2.2-1是一个简易的涂鸦板。

(图2.2-1)

这个涂鸦板有一个“Undo”按钮,可以删除刚刚绘制的一笔。在鼠标离开框架的时候,整个框架会使用一个模糊滤镜。例2.2-1是完整的程序。

package {
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.filters.BlurFilter;
import flash.geom.Point;

import org.aswing.ASColor;
import org.aswing.AsWingManager;
import org.aswing.BorderLayout;
import org.aswing.Container;
import org.aswing.FlowLayout;
import org.aswing.JButton;
import org.aswing.JFrame;
import org.aswing.JPanel;
import org.aswing.border.LineBorder;
import org.aswing.graphics.Graphics2D;
import org.aswing.graphics.IBrush;
import org.aswing.graphics.Pen;
import org.aswing.graphics.SolidBrush;

[SWF (width=600, height=500, frameRate=50)]
public class MouseTest extends Sprite {
private var shapes:Array = new Array();//在定义的时候对 Array 赋初值是一个好习惯

private var pen:Pen = new Pen(ASColor.BLACK, 3);
private var brush:IBrush = new SolidBrush(ASColor.BLACK);
private var lastPoint:Point;
private var drawing:Boolean = false;
private var currentShape:Shape;

private var frame:JFrame;
private var canvas:JPanel;

private var blur:BlurFilter = new BlurFilter();

public function MouseTest() {
super();

this.addEventListener(Event.ADDED_TO_STAGE, this.init);
}

private function init(e:Event) :void {
AsWingManager.initAsStandard(this);

this.frame = new JFrame(this, “Mouse Test”);
this.frame.setSizeWH(540, 400);

var c:Container = this.frame.getContentPane();

this.canvas = new JPanel();
this.canvas.setOpaque(true);
this.canvas.setBackground(new ASColor(0xFFFFFF));
this.canvas.setBorder(new LineBorder());
c.append(this.canvas, BorderLayout.CENTER);

var buttonPanel:JPanel = new JPanel(new FlowLayout());
c.append(buttonPanel, BorderLayout.WEST);

var undoBtn:JButton = new JButton(”Undo”);
undoBtn.addEventListener(MouseEvent.CLICK, this.undo);
buttonPanel.append(undoBtn);

this.configure();

this.frame.show();
}

private function configure() :void {
this.canvas.addEventListener(MouseEvent.MOUSE_DOWN, this.onMouseDown);
this.canvas.addEventListener(MouseEvent.MOUSE_UP, this.onMouseUp);

this.canvas.addEventListener(MouseEvent.MOUSE_MOVE, this.onMouseMove);

this.frame.addEventListener(MouseEvent.ROLL_OUT, this.onRollOut);
this.frame.addEventListener(MouseEvent.ROLL_OVER, this.onRollOver);
}

private function undo(e:MouseEvent) :void {
var shape:Shape = this.shapes.pop();

if (shape != null) {
this.canvas.removeChild(shape);
}
}

private function onMouseDown(e:MouseEvent) :void {
this.currentShape = new Shape();
this.canvas.addChild(this.currentShape);

this.lastPoint = new Point(e.localX, e.localY);
this.drawing = true;
}
private function onMouseUp(e:MouseEvent) :void {
if (this.currentShape != null) {
this.canvas.addChild(this.currentShape);
this.shapes.push(this.currentShape);
}
this.drawing = false;
}

private function onMouseMove(e:MouseEvent) :void {
if (this.drawing) {
var point:Point = new Point(e.localX, e.localY);

var g:Graphics2D = new Graphics2D(this.currentShape.graphics);
g.drawLine(this.pen, this.lastPoint.x, this.lastPoint.y, point.x, point.y);

this.lastPoint = point;
}
}

private function onRollOut(e:MouseEvent) :void {

this.drawing = false;
this.frame.filters = [this.blur];
}
private function onRollOver(e:MouseEvent) :void {
this.frame.filters = [];
}
}
}

我们来逐行分析这个程序。

在定义类的时候,我们用了一个[SWF (width=600, height=500, frameRate=50)]标记,这个标记告诉编译器目标SWF的大小是800像素宽,500像素高,帧速是50帧/秒。


笔记:

ActionScript3.0支持标记,除了SWF标记之外,常用的标记还有 [Event] 和 [Embed],在本书的附录当中将会介绍这些常用的标记。


在构造函数当中,我们侦听了一个ADDED_TO_STAGE事件,这个事件会在一个DisplayObject被添加到舞台上的时候被触发。和上一章的例子不同,从这一章开始,我们不再在构造函数当中直接做初始化,而是在ADDED_TO_STAGE事件的事件处理函数(init(Event):void {…})当中做初始化。因为在一个Sprite创建的时候它也许还没有被添加到舞台,这时候,Sprite的stage引用还没有被设置,这时候做初始化有可能会造成一些计算上的错误。所以我们将会在Sprite被添加到舞台上之后再来进行初始化。


注意!

在ADDED_TO_STAGE事件的处理函数当中进行初始化是一个良好的习惯,我们将建议你用这样的习惯。这会使你的程序有更好的兼容性。


我们用一个JPanel作为画布。这是因为JPanel是Sprite的子类,我们可以像操作Sprite一样操作JPanel。JPanel默认是透明的,为了能够在上面绘画,我们把它设为不透明的,并且将背景颜色设为白色(0xFFFFFF)。如下:

this.canvas.setOpaque(true);
this.canvas.setBackground(new ASColor(0xFFFFFF));

为了让画布的边缘更加明显,我们给画布加了边框(Border)。setBorder方法继承自Component,所有的AsWing控件都有这个方法。AsWing支持很多种边框,LineBorder是线状边框。常用作组件边界的标识。默认的LineBorder是直角,黑色,宽度为1像素的矩形线框。如下:

this.canvas.setBorder(new LineBorder());

每一笔的绘制从鼠标按下开始,到鼠标离开画布或者鼠标弹起结束。在鼠标按下的时候,我们新建了一个Shape对象,将这个对象加入到画布的显示队列当中(addChild),如下:

this.currentShape = new Shape();
this.canvas.addChild(this.currentShape);

为了记录绘图的过程,我们把每一笔作为一个Shape对象添加到画布(这时候,JPanel应当被看作是一个Sprite)的最上层。同时把这个Shape的引用加入到一个Array当中。在Undo的时候,我们从这个Array当中取出最新的一笔(Shape)的引用,通过这个引用从画布上删除这一笔。如下:

private function undo(e:MouseEvent) :void {
var shape:Shape = this.shapes.pop();

if (shape != null) {
this.canvas.removeChild(shape);
}
}


练习:

1.给这个画图板添加更多功能,比如绘制矩形、绘制椭圆。

2.你可以把整个绘制过程记录下来,以SVG保存。关于SVG的详细信息,可以参看SVG中国的文档http://www.chinasvg.com/


API:flash.events.Event

  • Event.ADDED_TO_STAGE:

在对象被加入到舞台(Stage)的显示队列当中的时候触发这个事件。一般来说,不要在构造函数当中直接初始化,而是在ADDED_TO_STAGE事件的处理函数当中初始化,这样可以避免一些初始化错误,程序的兼容性更强。

API:org.aswing.Component

  • setOpaque(b:Boolean):void

组件背景是否不透明,false为透明,true为不透明

  • setBackground(c:ASColor):void

设置组件的背景颜色,一般这个方法是和setOpaque配合使用的。只有背景是不透明的情况下才能看到背景颜色。

  • setBorder(b:Border):void

设置边框。

API:flash.events.MouseEvent

  • MouseEvent.CLICK

常数,鼠标点击事件,一次鼠标点击事件=鼠标按下+鼠标弹起。所以,鼠标按下事件和鼠标弹起事件会和鼠标点击事件一起被触发。

  • MouseEvent.MOUSE_DOWN

常数,鼠标按下事件,当鼠标在对象上方按下的时候触发。如果对象被

  • MouseEvent.MOUSE_UP

常数,鼠标弹起事件,当鼠标在对象上方弹起的时候触发

  • MouseEvent.MOUSE_MOVE

常数,鼠标移动事件,鼠标移动的时候触发。系统底层事件,两次事件之间的周期与帧速无关。

  • MouseEvent.ROLL_OUT

常数,鼠标滑出对象事件,鼠标滑出对象的时候触发。

  • MouseEvent.ROLL_OVER

常数,鼠标滑入对象事件,鼠标滑入对象进入对象上空区域的时候触发。

  • localX:Number

属性,鼠标当前的X坐标,参考系是当前对象的局部坐标系。

  • localX:Number

属性,鼠标当前的Y坐标,参考系是当前对象的局部坐标系。

API:flash.filters.BlurFilter

  • BlurFilter(blurX:Number=4.0, blurY:Number=4.0, auality:int=1.0)

构造函数,模糊滤镜。

API:flash.display.DisplayObject

  • filters:Array

属性,可读可写,滤镜列表,显示对象将依次使用队列当中的滤镜。

API:Array

  • pop() :Object

弹出队列尾端的元素(栈顶)。

  • push(o:Object) :void

把一个元素加到队列的尾端(栈顶)

Filed under: AsWing 教程 — thiswind @ 1:33 pm

AsWing入门教程 2.1 事件处理基础

作者:胡矿

著作权所有,请勿转载

www.flashseer.org

Google Doc

http://docs.google.com/Doc?id=dnp8gdz_24ctmwvs

任何Flash的应用都会不断监视敲击键盘、单击鼠标等事件。Flash播放器报告给正在运行的程序。然后程序会决定如何响应这些事件。在Flash8以及以前的时代,可以在影片剪辑里面直接写onXXX的事件处理代码,但是到了ActionScript3.0,这样的操作已经被废止了,对事件的处理都用统一的添加监听器的方式来实现。

事件源(EventDispatcher)拥有自己的方法,允许我们向其注册事件监听器。当事件源产生某个事件时,事件源会向注册在那个事件上的所有监听器发送通知。

在ActionScript3.0当中,所有的事件对象都是从flash.events.Event派生出来的。当然,有的事件类型有自己的子类,比如KeyboardEvent和MouseEvent。AsWing扩展了Event类,定义了许多自己的事件基类org.aswing.event.AWEvent,所有的AsWing自定义事件类都派生自AWEvent类,比如AttachEvent、DragAndDropEvent、FocusKeyEvent等等。

在ActionScript3.0当中,所有的事件源都是从flash.events.EventDispatcher派生出来的。Component类是EventDispatcher类的子类,所以AsWing组件都是事件源。例如,按钮会发送点击事件,而框架能够发送关闭窗口、最小化等窗口事件。

AsWing的事件机制是对ActionScript3.0事件机制的扩展,所以,AsWing的事件处理和ActionScript3.0的事件处理是相容的。

作为总结,下面给出AsWing当中事件处理机制的概览:

  • 一个监听器是一个方法。
  • 一个事件源是一个扩展了EventDispatcher,能够注册监听器并且向它们发送事件对象的对象。
  • 事件发生时,事件源会把事件对象发送给所有注册了这个事件的监听器,一个事件源可以发送多种不同的事件。
  • 监听器随后使用事件对象中的信息来决定对事件的反应。

可以使用下面模式的代码为事件源注册监听器:

eventSourceObject.addEventListener(eventType, listener);

例如:

var listener:Function = function (event:Event) :void {
……
};

var button:JButton = new JButton(”OK”);
button.addEventListener(MouseEvent.CLICK, listener);

现在,只要按钮中产生了一个“CLICK”事件,listener方法就会被调用。listener方法接受一个Event类型的参数,其中封装了CLICK事件的相关信息,比如事件所携带的数据,事件的引发者等等。

只要用户按下按钮,JButton对象就会产生一个MouseEvent对象,并且调用listener(event)传递那个事件对象。你可以为一个事件源添加多个监听方法。那样,只要用户按下按钮,所有的监听方法都会被调用。


笔记:

在ActionScript当中,方法(函数)也是对象,可以作为参数传递。


图2.1-1给出了事件源、事件监听函数和事件对象的写作关系。

(图2.1-1)


注意:

在本章中,我们重点关注AsWing的用户界面事件(如按钮点击和鼠标移动事件)的处理。然而,事件处理不仅限于用户界面。


2.1.1 实例:处理按钮点击事件

为了熟悉时间模型,我们使用一个响应按钮点击事件的简单例子来说明模型中所需了解的所有细节。在这个例子中,我们需要:

  • 一个放置了3个按钮的面板
  • 3个添加到按钮上,作为动作监听器的监听函数

在这个例子中,当用户点击面板上的任何一个按钮时,相关联的监听函数就会收到一个表明按钮被按下的MouseEvent对象。例子中的监听函数随后会改变面板的背景颜色。

在演示程序如何监听按钮点击事件之前,我们首先需要解释如何创建按钮,如何把他们添加到面板上。(有关GUI元素的详细内容,请参考第3章。)

可以通过在按钮的构造器中指定标签字符串、图标,或者二者都指定来创建一个按钮。下面是两个例子:

var button:JButton = new JButton(”Yellow”);
var button:JButton = new JButton(”", new AssetIcon(asset)); // 用一个 asset 作为图标。

把按钮添加到面板需要调用一个称作append的方法(很好记)。append方法的参数为需要添加到容器的组件。例如:

var buttonPanel:JPanel = new JPanel(new FlowLayout());

var yellowButton:JButton = new JButton(”Yellow”);
var blueButton:JButton = new JButton(”Blue”);
var redButton:JButton = new JButton(”Red”);

buttonPanel.append(yellowButton);
buttonPanel.append(blueButton);
buttonPanel.append(redButton);

buttonPanel.setOpaque(true);

图2.1.1-1显示了相应的效果。

(图2.1.1-1)

在上面的代码中,我们给面板设置了布局管理器(LayoutManager)。组件在添加到面板将会被放在哪里一般是由布局管理器来负责。对于JFrame和JWindow这样的顶层框架来说,默认的布局管理器是边界布局(BorderLayout)。JPanel默认的布局管理器为FlowLayout,在例子当中,我们明确的传入一个FlowLayout实例,你也可以传入其他任何布局管理器实例,FlowLayout是一个很常用的布局管理器,它将组件按照格子的最佳大小绘制,并且从左向右,从上倒下依次顺序排列。关于布局管理器在第3章还会详细叙述。由于默认情况下面板是透明的,我们要对其设置背景颜色就必须让其可见。通过调用buttonPanel.setOpaque(true)让面板变得可见。

现在我们已经布置好了按钮,下面我们要定义3个监听函数,并且分别设为每个按钮的监听函数:

private function __onYellowClick(e:MouseEvent) :void {

}

private function __onBlueClick(e:MouseEvent) :void {

}

private function __onRedClick(e:MouseEvent) :void {

}
yellowButton.addEventListener(MouseEvent.CLICK, this.__onYellowClick);
blueButton.addEventListener(MouseEvent.CLICK, this.__onBlueClick);
redButton.addEventListener(MouseEvent.CLICK, this.__onRedClick);


笔记:

因为在ActionScript当中,函数也是对象,所以监听函数即可以作为类当中的成员函数定义,也可以作为一个局部变量定义,如:

var listenr:Function = function (e:MouseEvent) :void { …… };


现在还遗留有一个问题,就是监听函数没有访问buttonPanel对象的权限。对于这个问题有很多种解决方法,在这里我们想通过我们的这种解决办法告诉大家如何使用事件对象。如下:

private function __onYellowClick(event:MouseEvent) :void {
var button:JButton = event.target as JButton;
var panel:JPanel = button.parent;
panel….
}

这种情况非常常见。事件处理函数往往需要执行一些动作来影响其他的对象。我们当然可以把需要被影响到的对象设为类的成员变量,这样在整个类当中它都将是可以见的。但是如果我们无法看见这个对象的时候,我们通常可以通过事件的发出者来找到所需的对象。event.target就是指向事件的发出者。因为我们把按钮放到了面板当中,所以按钮的父组件(parent)就是面板,于是我们通过button.parent就能得到对面板的引用。

例2.1.1-1包含了完整的程序。只要点击其中的按钮,相应的动作监听函数就会改变面板的背景颜色。

package
{
import flash.display.Sprite;
import flash.events.MouseEvent;

import org.aswing.ASColor;
import org.aswing.AsWingManager;
import org.aswing.Container;
import org.aswing.FlowLayout;
import org.aswing.JButton;
import org.aswing.JFrame;
import org.aswing.JPanel;

public class ButtonTest extends Sprite
{
public function ButtonTest()
{
super();

AsWingManager.initAsStandard(this);

var buttonPanel:JPanel = new JPanel(new FlowLayout());

var yellowButton:JButton = new JButton(”Yellow”);
var blueButton:JButton = new JButton(”Blue”);
var redButton:JButton = new JButton(”Red”);

buttonPanel.append(yellowButton);
buttonPanel.append(blueButton);
buttonPanel.append(redButton);

buttonPanel.setOpaque(true);

yellowButton.addEventListener(MouseEvent.CLICK, this.__onYellowClick);
blueButton.addEventListener(MouseEvent.CLICK, this.__onBlueClick);
redButton.addEventListener(MouseEvent.CLICK, this.__onRedClick);


var frame:JFrame = new JFrame(this, “Button Test”);
frame.setSizeWH(300, 250);


var c:Container = frame.getContentPane();
c.append(buttonPanel);

frame.show();
}

private function __onYellowClick(e:MouseEvent) :void {
var button:JButton = e.target as JButton;
var panel:JPanel = button.parent as JPanel;

panel.setBackground(ASColor.YELLOW);
}

private function __onBlueClick(e:MouseEvent) :void {
var button:JButton = e.target as JButton;
var panel:JPanel = button.parent as JPanel;

panel.setBackground(ASColor.BLUE);
}

private function __onRedClick(e:MouseEvent) :void {
var button:JButton = e.target as JButton;
var panel:JPanel = button.parent as JPanel;

panel.setBackground(ASColor.RED);
}
}
}

API:org.aswing.JButton

  • JButton(text:String=”", icon:Icon=null)

构造一个JButton对象,参数text是JButton的标签字符串,是一个普通的纯文本字符串,不支持HTML标记。

icon是按钮的图标。

API:org.aswing.Container

  • append(com:Component):void

把组件com添加到容器中。

API:org.aswing.AssetIcon

  • AssetIcon(asset:DisplayObject=null)

构造一个AssetIcon对象。asset是一个普通的DispalayObject,可以是一个Sprite,一个MovieClip,也可以是一个嵌入swf当中的图形元素

Filed under: AsWing 教程 — thiswind @ 1:31 pm

AsWing入门教程 第二章 事件处理

作者:胡矿

著作权所有,请勿转载

www.flashseer.org

Google Doc

http://docs.google.com/Doc?id=dnp8gdz_25cpkccc

事件处理在ActionScript3.0编程当中非常重要,ActionScript3.0是基于事件的。本章将介绍AsWing当中的事件处理,下一章将会讲述如何整合使用AsWing提供的大多数组件,并全面讲述这些组件所产生的事件。

Filed under: AsWing 教程 — thiswind @ 1:30 pm

Aswing入门教程 1.6 颜色和填充

作者:胡矿,iiley,Bill

著作权所有,请勿转载

www.flashseer.org

Google Doc

http://docs.google.com/Doc?id=dnp8gdz_16d63xzw

Graphics2D允许你利用刷子对象(Bursh)来进行颜色填充。如果要使用多种颜色来填充,那么你可以先选择一种颜色,进行填充;然后再选择另一种颜色,再进行填充。

ASColor类被用来定义颜色。org.aswing.ASColor类提供了代表16种常用颜色的常量值,如表1.6-1所示

表1.6-1

WHITE BLACK HALO_ORANGE MAGENTA
LIGHT_GRAY RED YELLOW CYAN
GRAY PINK GREEN BLUE
DARK_GRAY ORANGE HALO_GREEN HALO_BLUE

例如:

var brush:IBrush = new SolidBrush(ASColor.RED);
g2.fillRectangle(brush, x, y, width, height);

你可以通过创建ASColor对象来定制颜色,这时侯需要提供该颜色的红、绿、蓝颜色构成。三种颜色都是用0-255,即0×00-0xFF(也就是一个字节)之间的整数表示的,ASColor的构造器如下:

ASColor (rgb:uint=0×000000, alpha:Number=1)

下面是个定制颜色的例子:

var brush:IBrush = new SolidBrush(ASColor.getASColor(0, 128, 128)); // 灰暗的蓝绿色,也可以用new ASColor(0×008888)构建这个颜色
g2.fillRectangle(brush, 10, 10, 100, 40);

设置背景颜色的方法是使用Component类(JPanel类的祖先)中的setBackground方法。事实上,如果要看到背景颜色,你应该先将JPanel设为不透明。默认情况下,JPanel是透明的。对于AsWing的组件,你可以在运行当中随时改变其背景颜色。

var panel:JPanel = new JPanel();
panel.setOpaque(true);
panel.setBackground(ASColor.BLACK);
contentPane.append(panel);

LookAndFeel中通常都定义了组件的一些列默认颜色,这里称为系统颜色,可以通过UIManager.getColor(name)获取指定名称的颜色,比如UIManager.getColor(”window”)就会得到窗口背景的颜色,表1.6-2中是常用的系统颜色名称表。

表1.6-2 系统颜色

activeCaption 标题的背景颜色
activeCaptionText 标题的文本颜色
activeCaptionBorder 标题的边框颜色
inactiveCaption 非活动标题的背景颜色
inactiveCaptionText 非活动标题的文本颜色
inactiveCaptionBorder 非活动标题的边框颜色
window 窗口的背景颜色
windowBorder 窗口的边框颜色
windowText 窗口内文本的颜色
menu 菜单的背景颜色
menuText 菜单的文本颜色
text 文本的背景颜色
textText 文本的文本颜色
textHighlight 高亮文本的背景颜色
textHightliteText 高亮文本的文本颜色
control 空间的背景颜色
controlText 控件的文本颜色
controlLiHighlight 控件的轻高亮颜色
controlHighlight 控件的高亮颜色
controlShadow 控件的阴影颜色
controlDkShadow 控件的暗阴影颜色
scrollbar 滚动栏的背景颜色

API:org.aswing.ASColor

  • ASColor (rgb:uint=0×000000, alpha:Number=1)

创建一个颜色对象。

参数:rgb,用RGB颜色空间表示的颜色值。

alpha,颜色的透明度(0.0-1.0)

  • getASColor(r:uint, g:uint, b:uint, a:Number=1):ASColor

根据R、G、B的值获取一个新的颜色对象。

参数:r 红色值(0-255)

g 绿色值(0-255)

b 蓝色值(0-255)

a alpha,颜色的透明度(0.0-1.0)

  • getRGBWith(rr:uint, gg:uint, bb:uint):uint

根据R、G、B获取某一个颜色的颜色值

API:org.aswing.Component

  • setBackground(c:ASColor):void

设置背景颜色。背景颜色只有在组件为不透明的时候才会被显示。

参数:c 新的背景颜色。

  • setOpaque(b:Boolean):void

设置背景是否透明。

参数:b (truefalse)。

true表示不透明

false表示透明

填充形状

你可以用一个颜色来填充闭合形状(例如矩形或椭圆)的内部区域。只需用fill代替draw:

var brush:IBrush = new SolidBrush(ASColor.RED); // 创建一只红色的纯色刷子
g.fillRectangle(brush, 10, 10, 100, 100); // 填充矩形

刷子对象

你可以用刷子对象对形状做各种填充。所有的刷子对象都实现IBrush接口。

AsWing提供了3种刷子,他们分别是纯色刷子(SolidBrush)、过渡色刷子(GradientBrush)、位图刷子(BitmapBrush),下面我们分别介绍这三种刷子。

纯色刷子,这是最常用的一种刷子,它用一种颜色来填充区域,如图1.6-1所示,如下:

var solidBrush:IBrush = new SolidBrush(ASColor.RED);
g2.fillRectangle(solidBrush, bounds.x+10, bounds.y+10, 100, 100);

(图1.6-1)

过度色刷子可以表现颜色过渡效果。创建过渡色刷子需要提供过渡类型、颜色渐变范围和透明度范围。过渡类型线性过渡(GradientBrush.LINEAR)和圆形过渡(GradientBrush.RADIAL)两种。

线性过渡要先设定一个颜色列表,这个列表当中的颜色是将要参与渐变的各种关键颜色,如下是三个颜色的渐变色列表:

var colors:Array = [0×000000, 0xFF0000, 0×00FF00, 0×0000FF, 0×000000]; // 光谱黑-红-绿-蓝-黑,两端的黑色表示不可见波段

渐变色的透明度列表依次对应渐变色列表当中每一种关键颜色的透明度,在AS3.0当中,透明度不再用一个(0-100)的整数表示,而是改用一个(0-1)的小数表示,对于AS2程序员来说,这一点需要注意。如下:

var alphas:Array = [0, 1, 1, 1, 0]; // 两端的透明度为0,让填充区自然溶于背景当中

比率列表当中的每一项表示对应的一种关键颜色的100%采样值距离在填充框当中的位置比例。其中的每一个项是一个(0×00-0xFF)的数,填充的起点是0×00,填充的中止位置是0xFF。如下:

var ratios:Array = [0×00, 0×3F, 0×7E, 0xBD, 0xFF]; // 这5个数对 (0-0xFF)区间4等分,它们分别是: 0 * 0xFF/4, 1 * 0xFF/4, 2 * 0xFF/4, 3 * 0xFF/4, 4 * 0xFF/4

填充矩阵是一个矩阵(Matrix)对象。需要制定填充框的大小、填充的角度(默认为0度,即延x正方向从左到右填充)以及x、y方向的偏移量。一般来说,填充框的长和宽和要填充的图形的长宽相同,填充框的x,y偏移量分别就是要填充的图形的左上角定点的x,y坐标,如下:

var matrix:Matrix = new Matrix();
matrix.createGradientBox(100, 100, 0, bounds.x+10, bounds.y+120); // 填充框的大小为 100×100 像素,填充方向为水平从左向右,偏移量是绘图区域的左边缘向右10像素

在颜色列表,透明度列表,比率列表以及填充矩阵都定义好之后,就可以开始填充了,如图1.6-2,如下:

var linear:IBrush = new GradientBrush(GradientBrush.LINEAR, colors, alphas, ratios, matrix);
g.fillRectangle(linear, bounds.x+10, bounds.y+120, 100, 100);

(图1.6-2)


圆形填充(RADIAL)又叫做放射状填充,这种填充是从起点开始向周围所有方向填充,可以看到一圈一圈的等色线。

圆形填充的操作和线性填充一样,只要在填充的时候将填充类型设为圆形填充(GradientBrush.RADIAL)即可,如图1.6-3所示,如下:

var linear:IBrush = new GradientBrush(GradientBrush.RADIAL, colors, alphas, ratios, matrix);
g.fillRectangle(linear, bounds.x+10, bounds.y+230, 100, 100);

(图1.6-3)


例1.6-1是完整的示例代码,运行结果见图1.6-3

例1.6-1

package
{
import flash.display.Sprite;

import org.aswing.ASColor;
import org.aswing.Component;
import org.aswing.Container;
import org.aswing.JFrame;
import org.aswing.graphics.Graphics2D;
import org.aswing.graphics.IBrush;
import org.aswing.graphics.SolidBrush;


public class FillTest extends Sprite
{
public function FillTest()
{
var frame:JFrame = new JFrame(this);
frame.setSizeWH(400, 370);

var c:Container = frame.getContentPane();
c.setBackgroundDecorator(new MyCanvas());

frame.show();
}

}
}
import org.aswing.GroundDecorator;
import flash.display.Shape;
import org.aswing.Component;
import org.aswing.graphics.Graphics2D;
import org.aswing.geom.IntRectangle;
import flash.display.DisplayObject;
import org.aswing.graphics.IBrush;
import org.aswing.graphics.SolidBrush;
import org.aswing.ASColor;
import org.aswing.graphics.GradientBrush;

import flash.geom.Matrix;

class MyCanvas implements GroundDecorator {

private var shape:Shape = new Shape();

private var H_GAP:uint = 10;
private var V_GAP:uint = 10;

private var WIDTH:uint = 100;
private var HEIGHT:uint = 100;

public function updateDecorator(com:Component, g:Graphics2D, bounds:IntRectangle):void {
var g2:Graphics2D = new Graphics2D(this.shape.graphics);
g2.clear();

var rectBounds:IntRectangle = new IntRectangle();

//fill solid rect

rectBounds.x = bounds.x + H_GAP;
rectBounds.y = bounds.y + V_GAP;
rectBounds.width = WIDTH;
rectBounds.height = HEIGHT;

var solidBrush:IBrush = new SolidBrush(ASColor.RED);
g2.fillRectangle(solidBrush, rectBounds.x, rectBounds.y, rectBounds.width, rectBounds.height);

//fill liner grandient rect

rectBounds.y += HEIGHT; // move shape rect
rectBounds.y += V_GAP;

var colors:Array = [0×000000, 0xFF0000, 0×00FF00, 0×0000FF, 0×000000];
var alphas:Array = [0, 1, 1, 1, 0];
var ratios:Array = [0×00, 0×3F, 0×7E, 0xBD, 0xFF];
var matrix:Matrix = new Matrix();

matrix.createGradientBox(rectBounds.width, rectBounds.height, 0, rectBounds.x, rectBounds.y);
var linear:IBrush = new GradientBrush(GradientBrush.LINEAR, colors, alphas, ratios, matrix);

g.fillRectangle(linear, rectBounds.x, rectBounds.y, rectBounds.width, rectBounds.height);

//fill radial grandient

rectBounds.y += HEIGHT; // move shape rect
rectBounds.y += V_GAP;

matrix.createGradientBox(rectBounds.width, rectBounds.height, 0, rectBounds.x, rectBounds.y);
var radial:IBrush = new GradientBrush(GradientBrush.RADIAL, colors, alphas, ratios, matrix);

g.fillRectangle(radial, rectBounds.x, rectBounds.y, rectBounds.width, rectBounds.height);
}

public function getDisplay(c:Component):DisplayObject {
return this.shape;
}
}

位图刷子可以让你用位图图像来做填充,如图1.6-4:

var brush:IBrush = new BitmapBrush(bm.bitmapData, null, false, false); // bm是一个Bitmap实例
g.fillRectangle(brush, bounds.x, bounds.y, bounds.width, bounds.height);

(图1.6-4)


BitmapBrush构造函数有4个参数,形式为 BitmapBrush(bitmap:BitmapData, matrix:Matrix = null, repeat:Boolean = true, smooth:Boolean = false), 第一个参数bitmap即是要绘制的位图图像数据;第二个参数可以使绘制的图像进行变形,比如缩放、旋转、位移等;第三个参数指绘制的时候,如果绘制区域比图像大,是否进行平铺,在有些情况这个参数很有用,比如一个砖墙的背景,可以通过平铺一个很小的砖块图像形成一面墙的效果;第四个参数指定是否进行平滑处理。这几个参数它们的意义跟flash.display.Graphics.beginBitmapFill(bitmap:BitmapData, matrix:Matrix = null, repeat:Boolean = true, smooth:Boolean = false)中对应参数的意义一样,可参阅Flash CS3或者FlexBuilder帮助文档相关内容.

例1.6-2是完整的示例代码,窗口中放置了4个JPanel,他们用同一个位图图形,不同的参数进行绘制,用运行结果见图1.6-5

(图1.6-5)
例1.6-2

package{

import flash.display.Sprite;
import org.aswing.*;
import org.aswing.graphics.*;
import flash.geom.Matrix;

public class FillTest2 extends Sprite{

public function FillTest2(){
super();
AsWingManager.initAsStandard(this);
var frame:JFrame = new JFrame(this);

var p1:JPanel = new JPanel();
p1.setBackgroundDecorator(new MyBitmapDecorator(null, false, false));
var p2:JPanel = new JPanel();
p2.setBackgroundDecorator(new MyBitmapDecorator(null, true, false));

var p3:JPanel = new JPanel();
var matrix1:Matrix = new Matrix();
matrix1.scale(3, 3);
p3.setBackgroundDecorator(new MyBitmapDecorator(matrix1, false, false));

var p4:JPanel = new JPanel();
var matrix2:Matrix = new Matrix();
matrix2.rotate(Math.PI/4);
p4.setBackgroundDecorator(new MyBitmapDecorator(matrix2, true, true));

var pane:Container = new JPanel(new GridLayout(2, 2, 2, 2));
pane.appendAll(p1, p2, p3, p4);
frame.setContentPane(pane);

frame.setSizeWH(500, 400);
frame.show();
}

}
}

import org.aswing.*;
import org.aswing.graphics.*;
import org.aswing.geom.*;
import flash.display.*;
import flash.geom.Matrix;

class MyBitmapDecorator implements GroundDecorator{

[Embed(source=”img1.jpg”)]
private var imgClass:Class;
private var matrix:Matrix;
private var repeat:Boolean;
private var smooth:Boolean;

public function MyBitmapDecorator(matrix:Matrix = null, repeat:Boolean = true, smooth:Boolean = false){
this.matrix = matrix;
this.repeat = repeat;
this.smooth = smooth;
}

public function updateDecorator(com:Component, g:Graphics2D, bounds:IntRectangle):void {
var bm:Bitmap = new imgClass() as Bitmap;
var brush:IBrush = new BitmapBrush(bm.bitmapData, matrix, repeat, smooth);
g.fillRectangle(brush, bounds.x, bounds.y, bounds.width, bounds.height);
}

public function getDisplay(c:Component):DisplayObject{
return null;
}
}

API:org.aswing.graphics.SolidBrush

  • SolidBrush(color:ASColor)

创建一把纯色刷子。

API:org.aswing.graphics.GradientBrush

  • GradientBrush(fillType:String ,

colors:Array,

alphas:Array,

ratios:Array,

matrix:Matrix)

创建一把过渡色填充刷子。

参数:

fillType,填充类型,有线性填充(GradientBrush.LINEAR)和放射填充(GradientBrush.RADIAL)

colors,参与过渡填充的关键颜色列表。颜色用一个(0×000000-0xFFFFFF)之间的整数表示。

alphas,参与过渡填充的关键颜色的透明度的列表。透明度用一个(0-1)的小数表示。

ratios,参与过渡填充的关键色的位置比例。位置比例是一个(0×00-0xFF)的小数,0×00表示在填充的起点位置,0xFF表示在填充的结束位置。位置比例是一个比例,不是绝的像素位置。

matrix,填充矩阵,一般通过 matrix.matrix.createGradientBox(width, height, angle, x_offset, y_offset)方法来创建。

API:flash.geom.Matrix

  • createGradientBox(width, height, angle, x_offset, y_offset):void

创建一个填充矩阵。

参数:

width,填充框的宽度,单位是像素。

height,填充框的高度,单位是像素。

angle,填充的角度,单位是弧度

x_offset,填充框左上角的x坐标

y_offset,填充框左上角的y坐标

Filed under: AsWing 教程 — thiswind @ 1:29 pm

AsWing入门教程 1.5 2D图形

作者:胡矿

著作权所有,请勿转载

www.flashseer.org

Google Doc

http://docs.google.com/Doc?id=dnp8gdz_13dn73pw

从ActionScript3.0发布开始,Flash也有了专门绘制基本图形的工具类Graphics,可以用它来绘制直线、矩形、椭圆等。但是这些绘制非常有限。

Graphics2D类封装很多图形操作,用Graphics2D绘制图形很方便,例如图1.5-1的图形。

(图1.5-1)

如果你要得到一个Graphics2D对象,你只需要将一个普通的Graphics对象封装进去就行了,如:

protected function paint(bounds:IntRectangle) :void {
super.paint(bounds);

var g2:Graphics2D = new Graphics2D(this.graphics);

……
}

面板类(JPanel)是容器类(Container)的子类,它们从Component类继承了setBackgroundDecorator方法。setBackgroundDecorator方法允许用一个实现GroundDecorator接口的类作为JPanel的背景。JPanel默认的背景是透明的,就是它什么都不显示。我们可以通过扩展GroundDecorator类来改变JPanel的背景,对于图形编程我们推荐采用这种方式。

背景装饰器接口(GroundDecorator)是装饰器接口(Decorator)的子类,从Decorator接口当中继承了getDisplay方法,并且GroundDecorator接口还声明了updateDecorator方法,所以实现GroundDecorator就需要实现这两个方法,如下:

class MyBackground implements GroundDecorator {

private var shape:Shape;

public function MyBackground() {

this.shape = new Shape();

}

public function updateDecorator(c:Component, g:Graphics2D, bounds:IntRectangle) : void{
shape.graphics.clear();
g = new Graphics2D(shape.graphics);

……

}

public function getDisplay(c:Component) : DisplayObject{
return this.shape;
}
}

你可以直接用updateDecorator方法的Graphics2D参数进行绘制,但是我们建议新建一个Shape对象并且在新建的Shape对象上进行绘制,AsWing将通过getDisplay方法获取这个Shape对象,并且把它放置到容器当中。

扩展了GroundDecorator之后需要将扩展的对象作为JPanel的背景面板修饰器(BackgroundDecorator)。如下:

var ground:GroundDecorator = ……

panel.setBackgroundDecorator(ground);

容器组件(Container)包含一个内容窗格和一个背景窗格,如图1.5-1所示。在上一节当中,我们直接在内容窗格当中绘制,但是由于内容窗格同时也会放置其它组件,所以我们推荐在背景窗格上绘制。

(图1.5-1)

用Graphics2D绘制形状是很容易的。在绘制之前需要先创建一只笔(Pen)或者一把刷子(Brush),笔用来绘制线条,刷子则可以用来填充颜色。

构造一只笔(Pen)需要提供颜色和线条的粗细,如下代码定义了一支颜色为黑色,线条为2像素粗的笔:

var pen:Pen = new Pen(ASColor.BLACK, 2);


笔记:

在AsWing当中,颜色被封装到ASColor对象当中,ASColor定义了一些常用的颜色的颜色对象,比如ASColor.WHITE、ASColor.BLACK,如果需要定义特定的颜色,可以通过静态方法ASColor.getASColor来获取所需颜色的颜色对象,比如要得到红色(Red=0xFF,Green=0×0,Blue=0×0),可以用如下代码:

var red:ASColor = ASColor.getASColor(0xFF, 0×0, 0×0);

关于颜色的操作,在下一节还会继续介绍。


若要绘制一条直线,需要提供两个端点的坐标(x1,y1)和(x2,y2),如下:

g.drawLine(pen, x1, y1, x2, y2);

若要绘制一个矩形,需要提供左上角断点的坐标,以及矩形的宽度和高度,语法如下:

g.drawRectangle(pen, x, y, width, height);

对于椭圆的描述是通过它的外切矩形。所以若要绘制一个椭圆,需要提供其外切矩形左上角顶点的位置和这个外接矩形的长度、宽度,语法如下:

g.drawEllipse(pen, x, y, width, height);

绘制圆形的时候,需要提供的是圆心的坐标和圆形的半径,如下:

g.drawCircle(pen, x, y, radius);

例1-4中的程序绘制了:

  • 一个矩形;
  • 该矩形的内接椭圆;
  • 该矩形的一条对角线;
  • 一个和该矩形同心的圆。
  • 所有图形都绘制在框架的中央,如果框架改变大小,图形也会随之改变

例1-4 DrawTest.as

package
{
import flash.display.Sprite;

import org.aswing.AsWingManager;
import org.aswing.Container;
import org.aswing.GroundDecorator;
import org.aswing.JFrame;
import org.aswing.JPanel;

public class DrawTest extends Sprite
{
public function DrawTest() {

AsWingManager.initAsStandard(this);

var frame:JFrame = new JFrame();
frame.setTitle(”Draw test”);
frame.setSizeWH(DEFAULT_WIDTH, DEFAULT_HEIGHT);

var panel:JPanel = new JPanel();
var ground:GroundDecorator = new MyGroundDecorator();
panel.setBackgroundDecorator(ground);

var contentPane:Container = frame.getContentPane();
contentPane.append(panel);

frame.show();
}

public static const DEFAULT_WIDTH:uint = 400;
public static const DEFAULT_HEIGHT:uint = 300;
}
}

import flash.display.Shape;
import flash.display.DisplayObject;

import org.aswing.GroundDecorator;
import org.aswing.Component;
import org.aswing.graphics.Graphics2D;
import org.aswing.geom.IntRectangle;
import org.aswing.graphics.Pen;
import org.aswing.ASColor;

class MyGroundDecorator implements GroundDecorator {

private var shape:Shape;

public function MyGroundDecorator() {
this.shape = new Shape();
}

public function updateDecorator(com:Component, g:Graphics2D, bounds:IntRectangle) : void {
g = new Graphics2D(shape.graphics);

//clear before draw

g.clear();

//create a pen for drawing

var pen:Pen = new Pen(ASColor.BLACK);

//draw line

var x1:uint = bounds.x+100;
var y1:uint = bounds.y+50;

var x2:uint = bounds.x + bounds.width - 100;
var y2:uint = bounds.y + bounds.height - 50;

g.drawLine(pen, x1, y1, x2, y2);

//draw rectangle 1

var width:uint = x2 - x1;
var height:uint = y2 - y1;

g.drawRectangle(pen, x1, y1, width, height);

//draw ellipse

g.drawEllipse(pen, x1, y1, width, height);

//draw circle

var x:uint = bounds.x + Math.round(bounds.width/2);
var y:uint = bounds.y + Math.round(bounds.height/2);
var radius:uint = Math.round(Math.min(bounds.width, bounds.height)/2);

g.drawCircle(pen, x, y, radius);
}

public function getDisplay(c:Component) : DisplayObject {
return this.shape;
}
}

API:org.aswing.GroundDecorator

  • updateDecorator(c:Component, g:Graphics2D, bounds:IntRectangle) : void

更新装饰器的显示,这个方法将会在显示区域发上改变的适合哦自动调用,比如框架的尺寸发生改变的时候,还有第一次显示的时候。

参数c是被装饰的组件的引用,也就是这个装饰器的所有者。

g是c上面的2D图形对象(Graphics2D),通过g可以直接在c上绘制,不过我们推荐还是用自己创建的DisplayObject来绘制而不是直接用所有者组件的图形对象。

bounds是绘制的区域信息。你应该只在这个区域当中绘制。这个区域的大小不是固定的,在每次调用这个方法的时候,AsWing都会根据当前的显示布局重新调整bounds的值,所以你的程序应该根据bounds来设置大小

  • getDisplay(c:Component) : DisplayObject

获取DisplayObject类型的对象,如果你在绘制当中使用了自己新建的DisplayObject类型的对象,那么你应该在这里返回你的对象。

API:org.aswing.Container

  • setBackgroundDecorator(bg:GroundDecorator):void

设置背景装饰器,这个绘制发生在Container的背景层上。

API:org.aswing.graphics.Pen

  • Pen(color:ASColor,thickness:uint)

构造一支画笔。画笔是绘制线条的工具。

API:org.aswing.ASColor

  • ASColor (rgb:uint=0×000000, alpha:Number=1)

构造一个颜色对象。参数rgb是一个6位的16进制数,分别代表了Red、Green、Blue三个通道的数值。参数alpha是一个0到1之间的小数,控制颜色的透明度。0为完全透明,1为完全不透明。

  • getASColor(r:uint, g:uint, b:uint, alpha:Number=1):ASColor

利用三个通道的数值和透明多获取一个颜色对象

  • getASColorWithHLS(h:Number, l:Number, s:Number, alpha:Number=1):ASColor

通过HLS的三个通道和透明度获取一个颜色对象

API:org.aswing.graphics.Graphics2D

  • clear():void

清除已经绘制成的图像内容。

  • drawCircle(pen:IPen, centerX:Number, centerY:Number, radius:Number):void

绘制一个圆形。

  • drawEllipse(pen:IPen, x:Number, y:Number, width:Number, height:Number):void

绘制一个椭圆,椭圆的外切矩形的左上角定点位于(x,y),外切矩形的宽度为width,外切矩形的高度为height

  • drawLine(p:IPen, x1:Number, y1:Number, x2:Number, y2:Number):void

绘制一条直线段,(x1,y1)和(x2,y2)分别是线段的两个端点

  • drawPolygon(pen:Pen, points:Array):void

绘制一个多边形。points为顶点数组,数组当中的对象为的顶点对象,结构为{x:x0, y:y0}。多边形是封口的,最后一个定点和第一个定点之间会自动绘制连线。

  • drawPolyline(p:IPen, points:Array):void

绘制一个不封口的多边形边。points为顶点数组,和drawPolygon一样,区别只是DrawPolyline不会自动封口。

  • drawRectangle(pen:IPen, x:Number, y:Number, width:Number, height:Number):void

绘制一个矩形。矩形的左上角定点位于(x,y),宽度为width,高度为height

  • drawRoundRect(pen:IPen, x:Number, y:Number, width:Number, height:Number, radius:Number):void

绘制一个圆角矩形。矩形的外切矩形左上角顶点位于(x,y),宽度为width,高度为height。矩形的圆角的曲率半径为radius。圆角和边相切。

Filed under: AsWing 教程 — thiswind @ 1:28 pm

AsWing入门教程 1.4 在面板中显示信息

作者:胡矿

著作权所有,请勿转载

www.flashseer.org

Google doc

http://docs.google.com/Doc?id=dnp8gdz_63n8m3f

本节,我们将讲述如何在框架中显示信息。我们不再像刚开始学习时那样用 trace 将字符串输出到输出面板当中,而是要把它显示在一个框架里面,如图1.4-1所示。

(图1.4-1)

把信息直接输出到框架上是可能的,但是这么做被认为是不好的编程行为。在AsWing当中,框架实际上被设计为组件的容器,可以容纳菜单或者其他的用户界面元素。通常情况下,你需要在另一个组件上绘制信息,然后把它添加到框架中,这个组件称为面板。

JFrame 的结构并不是很复杂,图1.4-2说明了一个JFrame的组成。

(图1.4-2)

你可以看到,JFrame 当中安排了两个窗格,上面的是标题栏(Title Bar),下面的是内容窗格(Content Pane)。程序员最关心的是内容窗格,当设计一个框架时,组件会被添加到内容窗格中,使用的代码类似于:

var contentPane:Container = frame.getContentPane();
var c:Component = ……;
contentPane.append(c);

你不能直接往一个JFrame当中添加组件,而是应该向框架的内容窗格中放置。通过frame.getContentPane()可以得到框架的内容窗格。


笔记:

框架的内容窗格(Content Pane)是一个容器组件(Container)。Container是各种AsWing组件的容器,任何组件要可以显示,都必须添加到一个可以显示容器当中。 关于Container组件在后面的章节还会做详细的介绍。


在我们的例子中,我们想把一个面板添加到内容窗格中。面板是用JPanel类实现的。这种用户界面元素具有两个有用的特性:

  • 有一个能在上面进行绘制的表面
  • 本身也是一个容器

因此,它们还能容纳按钮、滑动条登其它界面元素。

不过,把一个普通的JPanel添加到内容窗格当中是没有什么意义的——它什么都不能做。要使它变得有意义,必须使用继承来创建一个新类,然后通过覆盖或者添方法的手段来获得所需的额外功能。

特别是,为了能在面板当中进行绘制,你需要:

  • 定义一个扩展(extends)JPanel的新类
  • 覆盖paint方法

paint方法实际上定义在Component中,这个类是所有AsWing组件的父类。该方法将在组件会绘制的时候被调用。你可以在该方法中调用JPanel的graphics对象进行绘制。graphics对象储存了一个用于绘制矢量图形的设置集合(比如颜色,填充方式,透明度)。paint方法的IntRectangle类型参数指出了绘制的范围,你应该确保绘制只发生在这个范围之内。在组件的大小发生变化的时候,这个范围会随着改变,在范围内绘制可以保证你的图形总是正确地显示。


笔记:

JPanel当中的Graphics对象继承自ActionScript3.0核心类当中的Sprite类。所有的AsWing组件都是Sprite类的子类,但是你应该只是在特定的方法当中获取graphics对象进行绘制。


下面的代码演示了如何创建一个可以在上面进行绘制的面板:

class HelloWorldPanel extends JPanel {

public function HelloWorldPanel () {

}

override protected function paint(b:IntRectangle) : void{

super.paint(b);

var g:Graphics = this.graphics;

…… // 在这里写绘制代码

}
}

只要窗口需要重绘,不管是因为什么原因,事件处理器都会通知组件。它会引起所有需要重绘的组件中的paint方法被执行。

绝不要自己调用paint方法。只要你的应用程序需要重新绘制,该方法就会自动调用。你不应该干涉这个过程。

哪些类型的动作触发了这个自动过程呢?例如,用户缩放窗口或者改变组件的显示相关的属性比如背景色、字体等会引起重绘。如果一个组件的外观和自己的状态相关,那么组件状态发生变化时,也会被要求重绘,比如按钮的由弹起状态变为按下状态。(当然,当窗口首次显示时,它也需要处理那些指定如何绘制、在哪里绘制初始元素的代码。如果你的图形尺寸是是依赖于paint方法的IntRectangle类型参数的,那么在窗口尺寸发生变化的时候,图形的尺寸也会随之调整以适应窗口。


笔记:

如果需要强制性重绘屏幕,那么可以调用repaint方法。这个方法会让屏幕立即重新绘制以显示新的内容。


在上面的代码段中可以看到,paint方法只有一个IntRectangle类型的参数。这个参数是一个矩形区域,它给定了绘制的范围。虽然AsWing并没有强制你只能在这个区域内绘制,但是你应该保证自己绘制的图形不要超出这个区域的边界。Graphics对象对屏幕的度量单位是像素。坐标(0,0)代表了你正绘制其表面的组件的左上角。

显示一个圆形是最基本的绘制之一。Graphics类有一个drawCircle方法,其语法如下:

g.beginFill(color);

g.drawCircle(x, y, radius);

在绘制之前,需要先设置颜色,否则Graphics对象将会用最后一次设置的颜色来进行绘制。颜色值是一个6位的16进制整数,每两位分别代表一RGB的个颜色通道。比如,0xFF0000就代表红色(R=FF,G=00,B=00)。我们的例子当中显示的圆形是黑色的,它的颜色代码就是0×000000。

在我们的例子中,我们需要在窗口的正中央绘制一个圆形。这个圆形正好内切框架的边缘。现在,我们的paint方法如下所示:

class HelloWorldPanel extends JPanel {

public function HelloWorldPanel () {

}

override protected function paint(bounds:IntRectangle) : void{

super.paint(bounds);

var x:uint = bounds.x + Math.round(bounds.width/2);
var y:uint = bounds.y + Math.round(bounds.height/2);
var radius:uint = Math.max(10, Math.round(Math.min(bounds.width, bounds.height)/2));

var graphics:Graphics = this.graphics;
graphics.beginFill(0×000000);
graphics.drawCircle(x, y, radius);

}

}

通过bounds可以获得绘制的区域大小,bounds会在框架大小发生变化的时候被重新设置以保证每次paint方法被调用的时候bounds都能正确反映绘制区域的大小,绘制的位置和尺寸应该根据bounds来计算。


注意!

由于HelloWordPanel扩展了JPanel类,而JPanel类在paint方法中有自己的操作。为了确保超类完成自己的那份工作,我们必须在进行我们的绘制之前先调用super.paint方法。



笔记:

如果你的程序有大量的绘制工作,那么你应该用另外一种方式来进行绘制,我们将在后面介绍Graphics2D的时候介绍这种方式。


例1-3给出了完整的代码

例1-3 HelloWorld.as

package
{
import flash.display.Sprite;

import org.aswing.AsWingManager;
import org.aswing.Container;
import org.aswing.JFrame;

public class HelloWorld extends Sprite
{
public function HelloWorld()
{
AsWingManager.initAsStandard(this);

var frame:JFrame = new JFrame();
frame.setTitle(”Hello World!”);
frame.setSizeWH(DEFAULT_WIDTH, DEFAULT_HEIGHT);

var contentPane:Container = frame.getContentPane();

var hello:HelloWorldPanel = new HelloWorldPanel();
contentPane.append(hello);

frame.show();

}


public static const DEFAULT_WIDTH:uint = 400;
public static const DEFAULT_HEIGHT:uint = 300;
}
}

import flash.display.Graphics;

import org.aswing.JPanel;
import org.aswing.geom.IntRectangle;

class HelloWorldPanel extends JPanel {
override protected function paint(bounds:IntRectangle) :void {
super.paint(bounds);

var x:uint = bounds.x + Math.round(bounds.width/2);
var y:uint = bounds.y + Math.round(bounds.height/2);
var radius:uint = Math.max(10, Math.round(Math.min(bounds.width, bounds.height)/2));

var graphics:Graphics = this.graphics;
graphics.beginFill(0×000000);
graphics.drawCircle(x, y, radius);
}
}

API:org.aswing.JFrame

  • getContentPane():Container

返回JFrame内容窗格对象,该对象是一个容器组件对象(Container)

API:org.aswing.Component

  • repaint():void

“尽可能快地” 重新绘制组件

  • paint(b:IntRectangle):void

需要覆盖该方法来说明你的组件应如何绘制。参数b给定了一个绘制的范围,你应该保证绘制在这个范围内进行。

Filed under: AsWing 教程 — thiswind @ 1:27 pm

AsWing 入门教程 1.3 给框架定位

作者:胡矿

著作权所有,请勿转载

www.flashseer.org

Google doc

http://docs.google.com/Doc?id=dnp8gdz_3gdr96f

JFrame类本身只有几个用来改变框架外观的类。当然,通过继承,JFrame从不同的超类中(比如JWindow, Component)继承来很多用于处理框架的大小和位置的方法:

  • dispose方法——关闭窗口并且回收用语创建窗口的任何资源;
  • setIcon方法——用一个Icon对象设置窗口的图标,比如AttachIcon 和 LoadIcon;
  • setTitle——改变标题栏当中的文字;
  • setResizable——使用 Boolean 参数来决定框架大小是否能被用户改变。
  • setClosable——使用 Boolean 参数来决定框架是否能被用户关闭。

图1.3-1当中给出了JFrame类的继承链。

(图1.3-1)

需要对 Component 类和 JWindow 类(它是JFrame类的父亲)进行仔细研究,在它们中能够找到缩放和改造框架的方法。例如,用于显示框架的show方法定义在JPopup类中。再例如,Component类中的setLocationXY方法是重定位一个组件的几种方法之一。如果你调用:

setLocationXY(x, y);

那么左上角被放到水平x像素、垂直y像素处((0,0)是屏幕左上角)。与此类似,Component中的setComBoundsXYWH方法允许你在一次操作中设定一个组件(特别是JFrame)的大小和位置,其形式为:

setComBoundsXYWH(x, y, width, height) ;



笔记:
对于框架,setLocationXY 和 setBoundsXYWH 中的坐标均是相对于整个屏幕的。在后面的章节还会看到,对于容器内的组件,其坐标是相对与容器的。


要记住:如果不明确定义框架的大小,那么其默认大小是0乘0像素。为了是例子程序尽可能简单,我们把框架设成能为大多数显示设置所能够接受的程度。


提醒:
本节所介绍的API是使框架具有正确观感的重要方法。这些方法的一部分定义在JFrame类当中;另一部分则定义在JFrame的各个父类当中。所以,你需要通过API文档来查找是否有完成特定目的的方法。遗憾的是,研究SDK文档相当费力。对于子类来说,API文档只是说明其覆盖的方法。例如,show对于JFrame类型的对象来说是可用的,但是由于它是从JPopup类继承而来,所以JFrame文档没有说明它。如果你认为应该有一个方法能够达到目的,但你使用的这个类的文档中却没有说明它,那么你应该在该类的超类API文档中查找这个方法。每个API页的顶部都有超链接到它的超类API页,并且每页在新方法和覆盖方法的总结下面都有一个继承的方法列表。

在本节末尾,我们给出一个简单的例子,以说明窗口操作。在例子中,那个可以关闭的框架具有如下特点:
1.其面积是整个舞台得四分之一
2.显示在舞台的中央


比如,舞台的大小是800×600像素,那么框架的大小就是400×300,它的左上角定位在(200, 150)。使用下面的步骤,可以得到舞台的大小。代码如下:

var bounds:IntRectangle = AsWingUtils.getVisibleMaximizedBounds();
var stageWidth:uint = bounds.width;
var stageHeight:uint = bounds.height;

完整的代码见例1.2。

例1.2

package
{
import flash.display.Sprite;
import org.aswing.JFrame;
import org.aswing.AsWingManager;

public class CenteredFrameTest extends Sprite
{
public function CenteredFrameTest()
{
AsWingManager.initAsStandard(this);

var frame:MyCenteredFrame = new MyCenteredFrame();
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.show();
}

}
}
import org.aswing.JFrame;
import org.aswing.geom.IntRectangle;
import org.aswing.AsWingUtils;
import org.aswing.LoadIcon;

class MyCenteredFrame extends JFrame
{
public function MyCenteredFrame()
{
//获取舞台可视区域的大小
var bounds:IntRectangle = AsWingUtils.getVisibleMaximizedBounds();
var stageWidth:uint = bounds.width;
var stageHeight:uint = bounds.height;

//把框架放在中心
this.setSizeWH(stageWidth/2, stageHeight/2);
this.setLocationXY(stageWidth/4, stageHeight/4);

//设置标题和图标
var icon:LoadIcon = new LoadIcon(”icon.png”);
this.setIcon(icon);
this.setTitle(”置于正中的框架”);
}
}

API:org.aswing.Component

  • isVisible() : Boolean
    检查组件是否可见。组件初始化为可见的,但顶层组件,如JFrame,除外。

  • s