Friday, August 13, 2010

Best Practice (my way) for managing "states" in ItemRenderer

When you have an ItemRenderer that has many states and the look/size of the ItemRenderer changes drastically between these states, what's the best way to manage them? What do you do when you need to maintain/keep track of the state of the ItemRenderer?

Because Flex framework reuses ItemRenderer for performance optimization, you can not maintain/keep track of the state in the ItemRenderer itself - ItemRenderer has to be stateless. The state of the ItemRenderer should be maintained by 'data' that drives it. The 'data' should keep enough information about the state of the ItemRenderer such that when ItemRenderer renders this 'data' it can determine which state it should go into.

Here an example -
ItemRendererA has two states 'a' and 'b'. The 'data' it renders is 'objectA'. 'objectA' has two vars - 'stateA' and 'stateB' that keeps track of this particular ItemRenererA state. Note: 'objectA' is part of the model and it persists state information.


<s:States>
  <s:state name='a'/>
  <s:state name='b'/>
</s:States>

// define boolean vars that will drive state changes
// State 'a'
private var _a:Boolean = false;
public function get a():Boolean
{
  return _a;
}
public function set a(value:Boolean):void
{
  if (value)
  {
    _a = value;
    _b = !value;

    // need to do this below to force a call to getCurrentRendererState()
    // ItemRenderer does not have 'invalidateRendererState()' function
    // which really sucks!
    setCurrentState(getCurrentRendererState(), true);
    if (autoDrawBackground)
    {
      redrawRequested = true;
      invalidateDisplayList();
    }
  }
}

// Do the same thing as above for state 'b'

// override set data function to set initial state
override public function set data(value:Object):void
{
  super.data = value;
  initializeRendererState();
}

private function initializeRendererState():void
{
  // 'data' here is 'objectA' which contains vars 'stateA' and 'stateB'
  if (data.stateA)
  {
    // setter for 'a' will force a call to getCurrentRendererState()
    a = true;
  }
  else if (data.stateB)
  {
    // setter for 'b' will force a call to getCurrentRendererState()
    b = true;
  }
}

// returns current state of the ItemRenderer
override protected function getCurrentRendererState():String
{
  if (a)
  {
    return "a";
  }
  else if (b)
  {
    return "b";
  }
}

Code above will make sure that ItemRenderer state is driven off of the 'data' that it's rendering and that ItemRenderer itself remain stateless.

0 comments:

Post a Comment