用Asroute解决复杂状态切换问题
项目地址: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解决复杂状态切换问题的更多相关文章
- Java线程生命周期与状态切换
前提 最近有点懒散,没什么比较有深度的产出.刚好想重新研读一下JUC线程池的源码实现,在此之前先深入了解一下Java中的线程实现,包括线程的生命周期.状态切换以及线程的上下文切换等等.编写本文的时候, ...
- WPF 编辑状态切换
有时候DataGrid编辑的时候一个属性需要根据别的属性呈现不同的编辑状态.这就需要一个做一个状态切换.比如地址是1的时候,读写类型是读写.只读.只写.地址是2的时候,就只读.状态栏切换为TextBo ...
- Java多线程系列1 线程创建以及状态切换
我们知道线程线程有三种创建方式 1实现Runnable接口 2 继承Thread类 3使用Callable和Future接口创建线程.具体是创建Callable接口的实现类,并实现clall()方法. ...
- Swift - UITableView状态切换效果
Swift - UITableView状态切换效果 效果 源码 https://github.com/YouXianMing/Swift-Animations // // TableViewTapAn ...
- Egret 中实现3种状态切换按钮
一.游戏中的常用3种状态按钮 Egret种提供了2种状态切换的按钮ToggleButton. 但是在游戏中常用到3种状态的按钮,比如任务系统的领取.已领取.未领取. 比如下图中宝箱的打开.浏览后打开. ...
- UITableViewCell状态切换效果
UITableViewCell状态切换效果 效果图 源码 https://github.com/YouXianMing/Animations // // TableViewTapAnimationCo ...
- Java线程状态切换以及核心方法
1.Java线程状态 1.1 线程主要状态 ①初始(NEW):新创建了一个线程对象,但还没有调用start()方法.②运行(RUNNABLE):Java线程中将就绪(ready)和运行中(runnin ...
- Java多线程的内存模型和Thread状态切换
线程的内存模型 32位操作系统的寻址空间为2的32次方,也就是4GB的寻址空间:系统在这4GB的空间里划分出1GB的空间给系统专用,称作内核空间,具有最高权限:剩下3GB的空间为用户空间(一般JVM的 ...
- Android ToggleButton:状态切换的Button
Android ToggleButton:状态切换的Button Android ToggleButton和Android Button类似,但是ToggleButton提供了一种选择机制,可以 ...
随机推荐
- 【Struts2学习笔记-4】包含其他配置文件
包含其他配置文件 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLI ...
- Nginx负载均衡和反向代理设置
Nginx负载均衡: 格式: upstream 别名 { #别名一般要有意义,能看出是做什么的 server ip:端口; #要实现负载的服务器的ip.端口号} 例: upstream ...
- pgsql 9.4修改数据库只读
先进入psql 切换到目标数据库 \c mydb 对于老表 grant usage on schema public to $read_only_user; grant select on all t ...
- SVN并行开发管理策略
总的原则:trunk保证相对稳定.分支合并到主干时将冲突降至最低. (1) trunk用于集成.测试.发布,可以提交fixbug代码,但不允许直接提交新特性. (2) 特性在分 ...
- 1.scala语法
对象的apply方法 (1)对象调用apply()方法,可省略成() (2)string对象的apply方法返回第n个字符 "hello"(4) //'o' if语句的返回值 ja ...
- try catch 怎么写?
除非必要,否则不在底层写try catch. 比如说,需要在catch里做一些处理,然后再抛出,一般不建议使用try catch掩盖程序出现的异常. try { BuildQueryComma ...
- 用radio控制<tr>的隐藏和显示问题
jsp页面代码 <tr> <th nowrap="nowrap" width="10%" height="50px" st ...
- DEV--GerdView控件
1.遍历 ; i < gridView1.RowCount; i++) { ; j < gridView1.Columns.Count; j++) { object val = gridV ...
- C++多态的实现及原理详细解析
C++多态的实现及原理详细解析 作者: 字体:[增加 减小] 类型:转载 C++的多态性用一句话概括就是:在基类的函数前加上virtual关键字,在派生类中重写该函数,运行时将会根据对象的实际类型 ...
- REDHAT6.2配置yum源(64位)(转载)
From:http://www.dedecms8.com/db/php_bc/12322.html 1.删除redhat原有的yum rpm -aq|grep yum|xargs rpm -e --n ...