用Asroute解決複雜狀態切換問題

項目地址:https://github.com/boycy815/asroutehtml

首先明確幾個概念git

狀態:github

不少狀況下,一個複雜的UI組件可能會有不少種不一樣的「狀態」,不一樣的「狀態」下組件自己對外界會有不一樣的行爲,外界根據組件的「狀態」會對其作不一樣的操做。網絡

狀態節點:函數

組件的「狀態」狀況由一系列「狀態節點」共同決定,每一個「狀態節點」都是一個只有兩種值的布爾值。通常「狀態節點」都會有必定意義,好比「在家」,「在公司」這是兩個地理位置維度上的「狀態節點」。若是「在家」這個「狀態節點」被選中(值爲true),那麼表示當前正在家裏。測試

子狀態:ui

某個「狀態節點」可能有更詳細的描述,舉個例子說明,若是「在家」算做一種「狀態節點」,那麼「在客廳」和「在廚房」應該算做「在家」的「子狀態」。「子狀態」被選中時,其父節點必定會先被選中。spa

互斥狀態:code

「互斥狀態」也是個「狀態節點」,他的「子狀態」不能同時被選中,一般狀況下這些「子狀態」都是對同一個維度的事實進行描述,好比「在客廳」和「在家」都是在地理位置進行描述,一我的不可能同時在客廳和在廚房吧。視頻

共存狀態:

「共存狀態」也是個「狀態節點」,他的「子狀態」容許同時被選中,一般他的「子狀態」描述的都是不一樣維度的事實,好比「在客廳」和「睡覺」。

 

咱們把上面介紹的幾種狀態節點組成樹形狀態,就能很好的描述當前系統處於什麼樣的狀態。下面咱們經過視頻播放器舉例說明

咱們須要播放器有「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必定是由於別的互斥狀態被選中而引發的。

相關文章
相關標籤/搜索