5 Replies Latest reply on Jul 8, 2009 3:56 PM by dave cragg

    DataGroup, ItemRenderer, custom states

    dave cragg Level 2

      I'm moving a half-completed app from FB 3 to FB 4, and experimenting along the way.

       

      I was interested in the DataGroup component and am using it for something previously done with a Repeater. It's for an object that indicates the status of items in an interactive quiz. In its basic form, each repeated object consists of a rectangle with a label in the center. The label displays the item number, and the color of the rectangle changes based on the status of the item (e.g. unanswered, answered correctly, answered incorrectly).

       

      I thought I could add some custom "states" in the item renderer to match the different statuses, like this:

       

      <s:states>

      <s:State name="normal"/>

      <s:State name="hovered"/>

      <s:State name="selected"/>

      <s:State name="wrong"/>

      <s:State name="right"/>

      </s:states>

       

      The first three are default requirements for an item renderer in a dataGroup. The "wrong" and "right" states are the new custom states. (I use the default state for the "unanswered" status.) Doing this, it was easy to set up alternative colors for the rect based on the current state.

       

      So if an item is answered wrongly, the currentState gets set to "wrong", and the appropriate color is used for the rectangle. It works fine until you then move the mouse over the rectangle. This changes the current state to "hovered" then "normal" and the color reverts. Not good.

       

      I couldn't find an obvious way to prevent the "hovered" state getting triggered. I ended up overriding the currentState setter function in the item renderer. This works, but it feels a bit inelegant. So my question, is there a more obvious way of preventing the "hovered" state getting triggered in a dataGroup?

       

      Of interst perhaps, I notice in the online docs ("Using view states with a custom item renderer"), there is an example that sets custom states like this:

       

      <s:states>

      <s:State name="normal"/>

      <s:State name="hovered"/>

      <s:State name="selected"/>

      <s:State name="default"/>

      <s:State name="showdesc"/>

      </s:states>    

       

      But the example doesn't make use of the custom states. It makes me wonder whether the author hit the same snag I did.

        • 1. Re: DataGroup, ItemRenderer, custom states
          deepa subramaniam (adobe) Level 2

          Hi Dave -

           

          Excellent, excellent question!

           

          Much like how a Spark component owns the skin states (ie: it is the responsibility of the component to put the skin in the proper state), the List component owns the item renderer states. To solve your problem with a skinnable component, you would simply override SkinnableComponent.getCurrentSkinState() so that your component puts the skin in the proper state based on mouse getstures, user data, etc. Though a Spark item renderer is not a skinnable component (it does not derive from SkinnableComponent) it very much acts like one - honoring all of the visual and state contracts that a skinnable component honors. As such, to solve your problem - the correct way is to override ItemRenderer.getCurrentRendererState() to return the correct state the renderer should be in based on the mouse interaction, user data, etc.

           

          So, your custom renderer's getCurrentRendererState() shouldn't return the hovered state since it doesn't seem as if you want to do anything visually to the item renderer when its hovered over.

           

          Overriding ItemRenderer.getCurrentRendererState() is the cleaner and more encouraged approach then overriding currentState setter in your renderer.

           

          PS - I noticed that the ASDoc for ItemRenderer.getCurrentRendererState() is @private, meaning that method does not show up in the AS documentation. Thats a bug! I'll fix it right now, but rest assured, that method is there for you to override. Take a look at it in source and do something similar for your usecase.

           

          Hope that makes sense,

          Deepa Subramaniam

          Flex SDK Engineer

          • 2. Re: DataGroup, ItemRenderer, custom states
            Ely Greenfield Level 1

            Actually, just to be pedantic - the list does not own the item renderer's states. The list sets some properties on the itemRenderer (defined in IItemRenderer).  The itemRenderer, in turn, sets its own state based on the value of those properties.  Your item renderer gets to change how its states are determined (as Deepa described), or you could implement an itemRenderer that has no states at all by implementing that interface on your own UIComponent.

             

            Ely.

            • 3. Re: DataGroup, ItemRenderer, custom states
              dave cragg Level 2

              Deepa, Ely, thanks for your responses. I learned some more from both of you.

               

              I tried both approaches, overriding the getCurrentRendererState() method and building a custom renderer from a UIComponent (in this case, by extending Canvas).

               

              On balance, overriding getCurrentRendererState seemed easier. But it's not exactly intuitive. You have to know ItemRenderer's "built-in" states before adding your own, and also know what getCurrentRendererState does before understanding that you have to override it. (Even if it had appeared in the docs, I'm not sure I would have thought to override it.) Originally I was looking for a property on dataGroup that would prevent the hovered and selected states from getting set. (such as "selectable" and "useRollOver" with the Halo List)

               

              Using a custom component required some trial and error. I started by using a Spark Group. But this doesn't implement IDataRenderer (which makes me think ItemRenderer is the "sanctioned" approach.) So I used Canvas. But if you want to use graphic primitives, you need a Group. So my component ended up something like this:

               

              <mx:Canvas>

              <mx:states> 

              <mx:State name="x"/>

              <mx:State name="y"/>

              <mx:State name="z"/>

              </mx:states>

              <s:Group>

              <s:Rect>

              ...

              </s:Rect>

              <s:SimpleText />

              </s:Group>

              </mx:Canvas>

               

              While I like a lot of the features of the Spark components, I'm finding I have to know a lot about the inner workings of the framework to make use of them.

              • 4. Re: DataGroup, ItemRenderer, custom states
                deepa subramaniam (adobe) Level 2

                Thanks Dave, for the feedback - I'm excited to hear it!

                 

                Do you think the non-intuitiveness of overriding getCurrentRendererState() could be diminished by having more custom examples of renderers in the documentation and online via blogs and cookbook recipes? We don't have as many code examples yet, that I'd like to see - and we're definitely looking to beef that up for release.

                 

                And yes, extending ItemRenderer for your custom renderer is the best-practice (ie: "sanctioned") approach. ItemRenderer already implements the necessary interfaces needed for a renderer to "play nice" in the Spark ecosystem (ie: ite implements IDataRenderer, IItemRenderer, etc). So by extending that and adding in your Canvases or Groups or graphics primitives, you're assured to be able to drop that renderer into any IItemRendererOwner (like a List, DataGroup, SkinnableContainer, etc) and be good to go.

                • 5. Re: DataGroup, ItemRenderer, custom states
                  dave cragg Level 2

                  I guess no two people will agree on how best to handle documentation. But here are a few rambling thoughts.

                   

                  Further examples are always good. However, examples in blogs and cookbooks can also confuse, especially if they take a different approach to things.   I generally think that such examples illustrate what you "can" do. I often look to such places for inspiration or help in solving a problem. But I think that "official documentation" should give out  stronger guidance of what we "should" do. This should certainly involve examples, but there are other things that help too.

                   

                  My first port of call is usually to the Language Reference pages. And this is where I'd hope to find any "must follow" guidance. So on the DataGroup reference, there is the line, "You must pass an item renderer to a DataGroup container.". That's what I like to see. But on the ItemRenderer page, there is little guidance on how to use this. Presumably people will want to subclass this a lot. But it doesn't say that you must declare the three states "normal, "selected", and "hovered" in your subclass. And it doesn't indicate how to introduce your own custom states. (I realise the docs here may be unfinished, and as you pointed out, the getCurrentRendererState entry is currently invisible. )

                   

                  But I guess my main confusion was not expecting ItemRenderer to have these initial built-in states. It seems more geared to Lists and not dataGroups. (But I may not have fully grasped the intent of the dataGroup container yet.) I think I would like to see a base ItemRenderer with no states implemented, and perhaps a subclass called ListItemRenderer which would be essentially like ItemRenderer is now. Then you could provide guidance to subclass the former when implementing a DataGroup where no selection or rollover  states are needed, and use the latter in Lists and DataGroups where you do want these states.

                   

                  Anyway, your help on this was much appreciated, and my DataGroup is working very nicely now.