项目地址: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. Env:Winmanager插件使用

    转自:http://www.cnblogs.com/feichexia/archive/2012/11/06/vim_WinManager.html 1.准备winmanger插件,从下面网址下即可: ...

  2. C#对数组去重

    #region ArrayList的示例应用 /// 方法名:DelArraySame /// 功能: 删除数组中重复的元素 /// </summary> /// <param na ...

  3. C#中abstract和virtual区别

    在C#的学习中,容易混淆virtual方法和abstract方法的使用,现在来讨论一下二者的区别.二者都牵涉到在派生类中与override的配合使用. 一.Virtual方法(虚方法) virtual ...

  4. Android Gradle实用技巧——APK文件名中加上SVN版本号,日期等

    有时候,我们会希望能把APK文件名上带上打包日期,打包时svn的版本号,应用版本号等.当然这些也可以手动添加,但是手动的话也未免太不优雅了,而且可能会出错. 利用Gradle,我们可以让打包出来的ap ...

  5. Android Gradle实用技巧——多渠道打包

    友盟有很多不错的功能,例如渠道统计等. 想要做渠道统计,有一个要求就是要在manifest文件中添加各个渠道的配置.只有一两个渠道还好说,但是渠道多了的话,手动修改然后打包简直是噩梦. 幸好现在And ...

  6. SQL语言的三个分类:DDL、DML、DCL

    DML:数据操纵语言,主要是完成数据的新增,修改,删除和查询的操作. DDL:数据定义语言,主要是用来创建或修改表.视图.存储过程以及用户等. DCL:数据控制语言,是用来设置或更改数据库用户或角色权 ...

  7. 使用BlockingQueue的生产者消费者模式

    BlockingQueue很好的解决了多线程中,如何高效安全“传输”数据的问题.通过这些高效并且线程安全的队列类,为我们快速搭建高质量的多线程程序带来极大的便利.使用场景. 首先它是一个队列,而一个队 ...

  8. Hololens开发笔记之使用Unity开发一个简单的应用

    一.Hololens概述 Hololens有以下特性 1.空间映射借助微软特殊定制的全息处理单元(HPU),HoloLens 实现了对周边环境的快速扫描和空间匹配.这保证了 HoloLens能够准确地 ...

  9. Mobirise

    网站建设器Mobirise v1.9.2 免费版 - 绿色软件联盟 2015年9月5日 - 网站建设器Mobirise是一个用户友好的程序,使您可以构建桌面和移动网站,准备在Javas cript中. ...

  10. java web 插件式开发

    1.支持pagelet2.菜单扩展3.静态文件访问 4.开发环境. 5.OSGi平台. tms:pagelet 是一个jsp标签 <div> <tms:pagelet show=&q ...