项目地址:https://github.com/boycy815/asroute

首先明确几个概念

状态:

很多情况下,一个复杂的UI组件可能会有很多种不同的“状态”,不同的“状态”下组件本身对外界会有不同的行为,外界根据组件的“状态”会对其做不同的操作。

状态节点:

组件的“状态”情况由一系列“状态节点”共同决定,每个“状态节点”都是一个只有两种值的布尔值。一般“状态节点”都会有一定意义,比如“在家”,“在公司”这是两个地理位置维度上的“状态节点”。如果“在家”这个“状态节点”被选中(值为true),那么表示当前正在家里。

子状态:

某个“状态节点”可能有更详细的描述,举个例子说明,如果“在家”算作一种“状态节点”,那么“在客厅”和“在厨房”应该算作“在家”的“子状态”。“子状态”被选中时,其父节点一定会先被选中。

互斥状态:

“互斥状态”也是个“状态节点”,他的“子状态”不能同时被选中,通常情况下这些“子状态”都是对同一个维度的事实进行描述,比如“在客厅”和“在家”都是在地理位置进行描述,一个人不可能同时在客厅和在厨房吧。

共存状态:

“共存状态”也是个“状态节点”,他的“子状态”允许同时被选中,通常他的“子状态”描述的都是不同维度的事实,比如“在客厅”和“睡觉”。

我们把上面介绍的几种状态节点组成树形状态,就能很好的描述当前系统处于什么样的状态。下面我们通过视频播放器举例说明

我们需要播放器有“close”,“inited”,“streaming”三种状态节点,其中close表示播放器未载入任何播放信息,inited表示已经载入播放信息随时可以准备开始播放,streaming表示已经连接,三种状态不能同时存在,所以是互斥的。

其中streaming状态带有“playing”和“buffering”两个子状态,分别从两个维度描述当前播放情况,属于共存状态。playing有play和pause是两个互斥状态,表示用户当前将播放器暂停或者播放;buffering有full和empty两个互斥状态,表示当前网络加载情况引起的状态。

这样的结构很好的描述了播放器的状态和各个状态节点之间的制约关系。下面我们通过asroute用代码把它描述出来。

var player:OrState = new OrState();
var close:State = new State(player);
var inited:State = new State(player);
var streaming:State = new State(player);
var playing:OrState = new OrState(streaming);
var play:State = new State(playing);
var pause:State = new State(playing);
var buffering:OrState = new OrState(streaming);
var full:State = new State(buffering);
var empty:State = new State(buffering);

OrState和State的构造函数参数为状态节点的父节点,一旦指定父级(或者不指定)就不能更改;OrState为State的子类,其子状态是互斥状态。

当某个状态被选中后发生事件的顺序:

当一个状态节点从未选中转变成选中时(值从false变为true),会发生StateEventConst.ENTER事件,从选中转变成未选中时(值从false变为true),会发生StateEventConst.EXIT事件。

如果一个状态节点在被选中的时候,其父级必定被选中。所以为了维持这个关系,如果一个状态节点即将被选中而其父级没有被选中,那么系统会自动先将其父级选中,这个规则会一直对其爷爷级祖宗级生效。另外如果对一个状态节点取消选中,那么在此之前其所有子状态也会先被取消选中。

OrState的特殊性在于,当其子状态被选中时,其他已被选中的子状态会先被取消选中。

可以通过下面的代码监听状态的改变

player.addEventListener(StateEventConst.ENTER, function(e:Event):void { trace("player enter") } );
player.addEventListener(StateEventConst.EXIT, function(e:Event):void { trace("player exit") } );
close.addEventListener(StateEventConst.ENTER, function(e:Event):void { trace("close enter") } );
close.addEventListener(StateEventConst.EXIT, function(e:Event):void { trace("close exit") } );
inited.addEventListener(StateEventConst.ENTER, function(e:Event):void { trace("inited enter") } );
inited.addEventListener(StateEventConst.EXIT, function(e:Event):void { trace("inited exit") } );
streaming.addEventListener(StateEventConst.ENTER, function(e:Event):void { trace("streaming enter") } );
streaming.addEventListener(StateEventConst.EXIT, function(e:Event):void { trace("streaming exit") } );
playing.addEventListener(StateEventConst.ENTER, function(e:Event):void { trace("playing enter") } );
playing.addEventListener(StateEventConst.EXIT, function(e:Event):void { trace("playing exit") } );
play.addEventListener(StateEventConst.ENTER, function(e:Event):void { trace("play enter") } );
play.addEventListener(StateEventConst.EXIT, function(e:Event):void { trace("play exit") } );
pause.addEventListener(StateEventConst.ENTER, function(e:Event):void { trace("pause enter") } );
pause.addEventListener(StateEventConst.EXIT, function(e:Event):void { trace("pause exit") } );
buffering.addEventListener(StateEventConst.ENTER, function(e:Event):void { trace("buffering enter") } );
buffering.addEventListener(StateEventConst.EXIT, function(e:Event):void { trace("buffering exit") } );
full.addEventListener(StateEventConst.ENTER, function(e:Event):void { trace("full enter") } );
full.addEventListener(StateEventConst.EXIT, function(e:Event):void { trace("full exit") } );
empty.addEventListener(StateEventConst.ENTER, function(e:Event):void { trace("empty enter") } );

控制状态节点改变:

每个状态节点都有个select方法,所以我们可以随意得选中我们想要的状态节点,系统会自动处理每个状态节点的关系,如

var ui:TextField = new TextField();
addChild(ui);
ui.width = 800;
ui.height = 600;
ui.htmlText = '<a href="event:player">player</a>\n<a href="event:close">close</a>\n<a href="event:inited">inited</a>\n<a href="event:streaming">streaming</a>\n<a href="event:playing">playing</a>\n<a href="event:play">play</a>\n<a href="event:pause">pause</a>\n<a href="event:buffering">buffering</a>\n<a href="event:full">full</a>\n<a href="event:empty">empty</a>\n';
ui.addEventListener(TextEvent.LINK, onLink); function onLink(e:TextEvent):void
{
if (e.text == "player")
{
player.select();
}
if (e.text == "close")
{
close.select();
}
if (e.text == "inited")
{
inited.select();
}
if (e.text == "streaming")
{
streaming.select();
}
if (e.text == "playing")
{
playing.select();
}
if (e.text == "play")
{
play.select();
}
if (e.text == "pause")
{
pause.select();
}
if (e.text == "buffering")
{
buffering.select();
}
if (e.text == "full")
{
full.select();
}
if (e.text == "empty")
{
empty.select();
}
} //自定义状态机跳转测试
inited.addEventListener(StateEventConst.ENTER, function(e:Event):void { close.select() } );
streaming.addEventListener(StateEventConst.ENTER, function(e:Event):void { close.select() } );
play.addEventListener(StateEventConst.EXIT, function(e:Event):void { play.select() } );

但是有一点是要注意的,你不能直接让一个状态节点的值置为false,因为状态节点的值被置为false一定是因为别的互斥状态被选中而引起的。

用Asroute解决复杂状态切换问题的更多相关文章

  1. Java线程生命周期与状态切换

    前提 最近有点懒散,没什么比较有深度的产出.刚好想重新研读一下JUC线程池的源码实现,在此之前先深入了解一下Java中的线程实现,包括线程的生命周期.状态切换以及线程的上下文切换等等.编写本文的时候, ...

  2. WPF 编辑状态切换

    有时候DataGrid编辑的时候一个属性需要根据别的属性呈现不同的编辑状态.这就需要一个做一个状态切换.比如地址是1的时候,读写类型是读写.只读.只写.地址是2的时候,就只读.状态栏切换为TextBo ...

  3. Java多线程系列1 线程创建以及状态切换

    我们知道线程线程有三种创建方式 1实现Runnable接口 2 继承Thread类 3使用Callable和Future接口创建线程.具体是创建Callable接口的实现类,并实现clall()方法. ...

  4. Swift - UITableView状态切换效果

    Swift - UITableView状态切换效果 效果 源码 https://github.com/YouXianMing/Swift-Animations // // TableViewTapAn ...

  5. Egret 中实现3种状态切换按钮

    一.游戏中的常用3种状态按钮 Egret种提供了2种状态切换的按钮ToggleButton. 但是在游戏中常用到3种状态的按钮,比如任务系统的领取.已领取.未领取. 比如下图中宝箱的打开.浏览后打开. ...

  6. UITableViewCell状态切换效果

    UITableViewCell状态切换效果 效果图 源码 https://github.com/YouXianMing/Animations // // TableViewTapAnimationCo ...

  7. Java线程状态切换以及核心方法

    1.Java线程状态 1.1 线程主要状态 ①初始(NEW):新创建了一个线程对象,但还没有调用start()方法.②运行(RUNNABLE):Java线程中将就绪(ready)和运行中(runnin ...

  8. Java多线程的内存模型和Thread状态切换

    线程的内存模型 32位操作系统的寻址空间为2的32次方,也就是4GB的寻址空间:系统在这4GB的空间里划分出1GB的空间给系统专用,称作内核空间,具有最高权限:剩下3GB的空间为用户空间(一般JVM的 ...

  9. Android ToggleButton:状态切换的Button

     Android ToggleButton:状态切换的Button Android ToggleButton和Android Button类似,但是ToggleButton提供了一种选择机制,可以 ...

随机推荐

  1. 剑指offer系列19--栈的压入、弹出序列

    题目描述 输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序.假设压入栈的所有数字均不相等.例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序 ...

  2. 常用JS

    window.opener.location.reload(); 刷新上一个页面 window.opener=null;window.open('','_self');window.close(); ...

  3. VBA相关

    --能否彻底隐藏某行或某列 用代码隐藏列,将其放在Private Sub Worksheet_SelectionChange(ByVal Target As Range)Columns(1).Enti ...

  4. 导航栏4种效果---原生js

    <!doctype html> <html> <head> <meta charset="utf-8"> <title> ...

  5. 黄聪:WordPress根目录(Root)

    index.php:WordPress核心索引文件,即博客输出文件. license.txt:WordPress GPL许可证文件. my-hacks.php:定义了博客输出之前处理的追加程序.默认安 ...

  6. Spark读写Hbase中的数据

    def main(args: Array[String]) { val sparkConf = new SparkConf().setMaster("local").setAppN ...

  7. RabbitMQ介绍4 - 编程(C#客户端示例)

    C#终端的说明文档: http://www.rabbitmq.com/dotnet-api-guide.html 这里介绍使用RabbitMQ的几种典型场景. 1. 简单direct模式( http: ...

  8. 优化studio的速度

    随着Android Studio开发工具的逐渐成熟,越来越多的程序员选择这种IDE工具来进行开发,但是android studio在使用过程中有时候会出现卡顿问题.在赶项目的时候,遇到这类问题最是苦恼 ...

  9. 带节日和农历的js日历 带农历的脚本:

    <html><head><meta http-equiv="Content-Type" content="text/html; charse ...

  10. Python补充03 Python内置函数清单

    作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明. Python内置(built-in)函数随着python解释器的运行而创建.在Pytho ...