Home > Custom Layout > FlowLayout – a Spark Custom Layout Example

FlowLayout – a Spark Custom Layout Example

I’ve been working on the Flex SDK team for more than a year now, primarily playing with the Spark layouts.  One of the new Spark features is the “assignable layouts”.  The stock layouts have been separated into their own classes and the containers can be assigned different layouts at run-time.  This modularization allows anyone to create their own cool custom layouts and use them with any of the Spark containers.

I want to show how easy it is to create a custom Spark layout.  I’ve chosen a flow layout example where all the elements are arranged horizontally, wrapping to the next row when the width limit of the container is reached.  For simplicity I’m going to assume that all elements are of equal height.

Update: Check out part 2 of the article here.

Step 1 – Simple List Application with a Stock TileLayout

For starter, let’s get a simple application going with a Spark List with a few items. I’m going to add a slider that controls the width of the List. By default the List’s layout is the VerticalLayout, but I’m going to switch it to the stock TileLayout just to contrast it with the FlowLayout that I’m going to put together further below.

And the source code:

List with TileLayout

FlowTest.mxml

Step 2 – A Custom Layout Skeleton Class

Next step is creating the skeleton for the custom layout. I’ll create a new AS class FlowLayout.as, and since it is a custom layout, it must subclass LayoutBase.  The LayoutBase provides a lot of default functionality, but the most important methods I need to override are updateDisplayList() and measure().

  • updateDisplayList() is called by the container to size and arrange the container’s elements.
  • measure() is called to calculate the default size of the container based on the container’s elements. I’m not going to override measure(), as the List skin already has default size.
FlowLayout.as

FlowLayout.as

Step 3 – Make Sure It Does Something

Now I’m going to iterate through all of the container’s elements.  I’ll access the container through the layout’s “target” property, which is of type GroupBase.  To work with the elements I’ll be using the ILayoutElement interface, specifically the following APIs:

  • setLayoutBoundsSize(width, height) – where I can also pass in NaN leaving the element to size itself to its preferred size.
  • getLayoutBoundsWidth(), getLayoutBoundsHeight() – to get the current bounds of the element.
  • setLayoutBoundsPosition(x, y) – to position the element.  Coordinates are relative to the container’s origin.

Before delving into too much coding, let’s just size every element to it’s preferred size, stagger them vertically by 10 pixels each and setup the List to use the the new FlowLayout.

And the source code:

a-simple-flow-layout-step3

FlowLayout.as

The update to the flowText.mxml

The update to the flowText.mxml

Final Step – Finish the FlowLayout

And now let’s finish the FlowLayout – I want to arrange elements left to right, space them by 10 pixels, and move the elements down when there’s not enough space to fit in the containerWidth.  With a couple of lines I get the final result:

And the final code looks like this:

Final source code of the FlowLayout.as

Final source code of the FlowLayout.as

Hope you enjoyed it!

Download the source files here.

Tags: ,
  1. June 5th, 2009 at 07:54 | #1

    In the finished example at the top of the screen, the list got a scroll bar when the items didn’t fit entirely in the box. The example at the bottom does not. How do you force the scrollbar to appear when needed?

    • Evtim
      June 6th, 2009 at 00:03 | #2

      Hey Rob, good point! In general, to get scrolling, you need scroll bars that are bound to the horizontalScrollPosition and verticalScrollPosition layout properties. You’d also need to configure the scroll bars’ scrolling range. Specifically in Flex4 we made that easy, as we implemented most of the functionality in the Scroller. The Scroller automatically shows/hides the scroll bars and binds them to the scroll position of the layout. Also, the List skin already has a Scroller by default!
      The only thing that is missing from the FlowLayout code is calculating the actual scrolling range. In the layout you need to calculate the size of the content area (usually maximum x/y of the) and set it on the layoutTarget through the setContentSize() method.
      So adding the following code as the very last line (after the loop) in the FlowLayout.updateDisplayList() should do the trick:

      layoutTarget.setContentSize(x, y + elementHeight);

      -Evtim

  2. June 5th, 2009 at 14:51 | #3

    I would be interested in how to do “grouping” using the new layout.

    Cheers
    Gareth.

    • Evtim
      June 6th, 2009 at 00:30 | #4

      Hi Gareth,
      what do you mean by “grouping”, can you give me a specific example?

      -Evtim

  3. June 5th, 2009 at 15:22 | #5

    So…the layouts have css properties, right? So you could set FlowLayout css properties for things like horizontalGap to replay elementWidth+10, right?

    Just curious.

    • Evtim
      June 6th, 2009 at 00:28 | #6

      Hey John,
      Generally our Flex4 layouts have properties and don’t use css styles. If you take a look at the FlowTest.mxml the Application’s VerticalLayout has its paddingTop, gap and horizontalAlign properties explicitly specified, these are not css styles, but properties on the VerticalLayout class itself. For a custom layout you can also follow this pattern, or if you really want styles, you could do that as well, but you’ll need to do more work to make sure the layout is part of the style chain and you detect when a style changes. One reason for our choice to do properties for layout configs was that it was obvious which are the supported properties for a particular layout and also this way it was more extensible – we didn’t have to pre-define a set of layout-related styles.

      -Evtim

  4. Glenn Williams
    June 6th, 2009 at 04:05 | #7

    HI Evtim,

    Glad to see you started a blog.

    I’ve just watched the flashcamp presentation Ely gave where he used a demo of a 3d wheel layout you did. lovely.

    any chance of making the source for that available? Would love to have a look through it.

    feel feel to drop me an email, would love a chance to talk.

    kindest regads
    glenn
    tinylion

  5. June 6th, 2009 at 11:52 | #9

    Hi Evtim,

    Thanks for the example. Gumbo is very cool

  6. glenn
    June 11th, 2009 at 01:40 | #10

    HI Evtim,

    thanks for that. Funny thing is, i’ve been using nightly builds of the sdk every day since u first put it all online., yet i’ve never looked into the test code, maybe i should spen a few minutes having a liitle digg around in there. 🙂 never know what interesting things i may find.

    cheers for the heads up.

    great work by the way. been using sdk4 since early early alphs, and love it. its a real step forward in my opinion. great stuff

    all the best and thanks again

    glenn
    tinylion development uk

  7. June 11th, 2009 at 15:52 | #11

    @Evtim

    Ok, that works. Thanks for the explanation.

  8. superabe
    June 24th, 2009 at 15:40 | #12

    Thanks for the posts, very enlightening. Any chance you can post the source for the 3d layout soon ?

  9. superabe
    June 26th, 2009 at 16:02 | #14

    Thanks for posting the well example. I notice that both in the wheel and the flowlayout (swf at the end), key board navigation does not seem to work while for the flowlayout at the top of the page, key board navigation (selection) does work.

    What would we need to do to enable keyboard nav/selection?

    • Evtim
      June 28th, 2009 at 12:34 | #15

      Hi superabe, that’s a good observation!
      The example where the keyboard works uses the stock Spark TileLayout. For all other examples, I’m using custom layouts for which I didn’t implemented keyboard navigation. To make keyboard navigation work, you need a container that supports it (List supports navigation) as well as a layout that supports it. In a custom layout to get navigation working, you’d need to override the LayoutBase getNavigationDestinationIndex() method (in the Flex 4 beta, it’s name is different – getDestinationIndex()). Basically the container calls the layout’s method passing current index and a keycode. The layout calculates which item the list needs to navigate to. For example in a TileLayout pressing the “left” will return the index of the element on the same row, but one column to the left. You can take a look at how this function is implemented for TileLayout for example. The latest post-beta version can be found here http://opensource.adobe.com/svn/opensource/flex/sdk/trunk/frameworks/projects/spark/src/spark/layouts/TileLayout.as. Let me know if you need more info.

  10. superabe
    July 7th, 2009 at 15:02 | #16

    Thanks for the tip. I was able to extend the HorizontalLayout class to get the layout I want while leveraging the existing keyboard navigation

  11. July 26th, 2009 at 08:20 | #17

    Hi Evtim. I’m hoping you can help me. I want to create a ‘PagedList’ component that will automatically create pages of its data based on the visual implementation of the data. Do I need to control this within a custom Layout or is this not part of the scope of a ‘paging’ mechanism. I don’t feel that paging really counts as layout. I’m wondering how to approach this. Do you have any thoughts?

    • Evtim
      July 28th, 2009 at 23:32 | #18

      Hi Lee,
      I actually haven’t played with data paging. Totally off the top of my head, I’d imagine that you’d need to subclass DataGroup. There you can override the method
      getVirtualElementAt(index:int):IVisualElement. This method is called by the layouts to get the item renderer for the item with the specified index. In your case, if this element is not yet paged in, you can probably return an instance of a special item renderer that will be your placeholder for the not yet loaded item. Probably on updateComplete() for your component you can send a page in request and update your data.
      If that works, you could use your subclass, say PagedDataGroup, as a skin part in a skin for your component, probably even for a custom skin for the Spark List. These changes may also require some modifications in the DataGroup itself, but I don’t know at this time as I haven’t explored it.

      I’d also like to refer you to the Adobe open source forums, the ones for the Flex 4 beta here http://forums.adobe.com/community/labs/gumbo/ or some more general ones here http://opensource.adobe.com/wiki/display/site/Forums.

      -Evtim

  12. July 28th, 2009 at 14:29 | #19

    Hi Evtim,
    I’m working on an app that has to be able to switch between western and RTL (Arabic) language/layout during runtime.
    It’s a classical business app with bunch of tables, text fields, buttons scattered all around the screen and when turning to Arabic entire UI has to mirror.
    I’m doing a research on the best way to accomplish this functionality and I’d deeply appreciate if you have any tips about the path to walk regarding layout engine.
    Cheers,

    Tomislav

    • Evtim
      July 28th, 2009 at 23:30 | #20

      Hi Tomislav,
      Some form of RTL has been on our list, though I don’t know when we’ll be able to get to it. Some examples I’ve seen doing this use negative scaling and x offset on the components to flip them. You’d have to be careful which components are flipped and which are not as you walk down the display tree. Also you can probably use the “offsets” property to modify the post-layout transform. Also, you can take a look at the Flex 4 beta forums here for more insights on the topic http://forums.adobe.com/community/labs/gumbo/

      -Evtim

  13. July 29th, 2009 at 09:13 | #21

    Hi,
    thanks for tips. I did a simplest possible solution by extending BasicLayout and my updateDisplayList() looks like this:


    super.updateDisplayList(width, height);

    //read direction from CSS
    if (target.getStyle("direction") == 'rtl') {
    for (var i:int = 0; i < target.numElements; i++) {
    var x:Number;
    var y:Number;

    var element:ILayoutElement = target.getElementAt(i);

    x = width - element.getLayoutBoundsWidth() - element.getLayoutBoundsX();
    y = element.getLayoutBoundsY();

    element.setLayoutBoundsPosition(x, y);
    }

    It works well except that sometimes I get items that are not affected by mirroring after changing layout preferences during runtime…

  14. August 10th, 2009 at 16:08 | #22

    Five blls
    Very useful, bookmarked
    Regards – many thanks for this

  15. Anil Gupta
    August 12th, 2009 at 02:15 | #23

    i want to create application in flex (mxml)i want to design like,when i click on button the left box should be shifted in left please help me

    • Evtim
      August 29th, 2009 at 14:28 | #24

      Hi Anil,
      I think that you probably just need to use a BasicLayout container and add a click event that will change the left constraint (or x coordinate) on your box.
      Take a look through the Flex SDK documentation, and additionally you can search the forums here .

  16. September 30th, 2009 at 21:12 | #25

    You’ll notice that if you make the width really small (or the elements really big) the first item is bumped down one row. So I found myself writing:

    if (i>0) { y += elementHeight; }

    to never move the first element down.

  17. May 20th, 2010 at 22:23 | #27

    hi,

    I am new For this flex please help me in this type of application Product that i can start from the begining.

    Thanks and Regards,
    Bridge Logic System Sofwtare Pvt Ltd.
    9971466260
    http://www.bridgelogicsystem.com/

  18. July 5th, 2010 at 22:47 | #28

    @Evtim

    Hey – quick question – I’m having some trouble with a Flex 4 layout that I want to be liquid, 100% width AND height. Trouble is, I’m having a hard time getting my containers to clip properly when I resize the browser window, while the outermost container resizes correctly, the inner ones (where I have a scroller attached) seem to ignore the resizing and don’t clip properly.

    Has anyone dealt with this issue and have a solution? Of course I could just do a fixed height, but that wouldn’t be ideal.

    • Evtim
      September 18th, 2010 at 11:37 | #29

      Hi Matthew,
      I’d have to see an example of your container arrangement to be able to tell if there’s something wrong or perhaps we have a bug in the SDK 😉
      Some issues that we see more often than others:
      – make sure to set the 100% on the Scroller itself, and not on the container inside the Scroller.
      – make sure that the container doesn’t have some explicit or measured minimum size that will prevent it to shrink below that point. Easiest way is to explicitly set some minWidth=”0″, minHeight=”0″

  19. Yan Xuehe
    September 15th, 2010 at 00:26 | #30

    how to add effects on invoking setLayoutBoundsSize() and setLayoutBoundsPosition()?

  20. Arjen
    September 24th, 2010 at 04:30 | #31

    Hi,

    When i create your example i keep on running into the problem that the “layoutTarget.getElementAt(i);” returns null.

    Do you have any idea’s on what i’m doing wrong?

  21. Arjen
    September 24th, 2010 at 04:52 | #32

    @Arjen
    Figured it out myself, since is was using a itemrenderer i needed to use “getVirtualElementAt”

  22. October 21st, 2010 at 16:12 | #33

    I just read a few other posts on here. Glad I checked it out again. Nice work

  23. Dee
    October 29th, 2010 at 18:38 | #34

    Hi actually I am developing an Flex album. I am using List to display thumbnail images. I have applied tile Layout. But the thing is I need to specify padding top and bottom. How can i implement custom extended Tile Layout with this feature. Can you give me some tips

  24. ash
    December 16th, 2010 at 12:49 | #35

    What about scroll bars and custom layouts? How difficult are they? Specifically: http://forums.adobe.com/thread/765682?tstart=0

  25. Maurice
    January 9th, 2011 at 05:29 | #36

    Hi Evtim, I need the flow layout to fill 100% of each row width, possibly enlarging the items that it contains.
    Example: suppose the first row contains 3 items of width 100, 200 and 300 (total = 600) and the layout width is 800, then resize the items to 133, 267 and 400, so that the total is now 800.
    Same for the other rows.
    Is that possible?

    • Evtim
      April 19th, 2011 at 19:44 | #37

      @Maurice
      Yeah, resizing the items to fill in the total width of the row should be possible, you’d need to touch the logic in updateDisplayList() – you already know the total width, then calculate the percentage of each element based on element’s preferredWidth() / sum (all element’s preferred widths) and apply that percentage to the “width” that gets passed in the updateDisplayList() method.

  26. Jim
    September 29th, 2011 at 05:29 | #38

    Hi Evtim, I am trying to create a org chart using flex 4 layouts. I basically want to write 1 rectangle at level 0, 2 rectangles at level 1 and 4 rectangles at level 2 and connect them with lines. I want to call the layout twice, once to draw the rectangles and once to draw the lines. How do you know what the x and y coordinates are for components after drawn using the layout.

  27. Raja Kumar
    March 18th, 2012 at 07:57 | #39

    Actually you have wrong height measurement. You should override and write something sensible to measure(). Check this out http://saprahan.blogspot.com/2012/03/adobe-flex-flow-tabs-layout.html

  28. Zant
    June 20th, 2012 at 07:17 | #40

    absolutely genius!

  29. June 21st, 2017 at 19:07 | #41

    Hi there,I read your blogs named “Easy Flex » FlowLayout – a Spark Custom Layout Example” on a regular basis.Your story-telling style is awesome, keep up the good work! And you can look our website about proxy free list.

  30. June 30th, 2017 at 21:40 | #42

    I must say you have hi quality content here. Your posts should go
    viral. You need initial boost only. How to get
    massive traffic? Search for; Murgrabia’s tools go
    viral

  31. June 30th, 2019 at 00:20 | #43

    Everything is very open with a very clear explanation of the issues.

    It was really informative. Your site is useful. Many thanks for sharing!

  1. June 5th, 2009 at 13:18 | #1
  2. June 5th, 2009 at 15:02 | #2
  3. June 5th, 2009 at 15:10 | #3
  4. June 5th, 2009 at 23:06 | #4
  5. June 10th, 2009 at 23:53 | #5
  6. August 13th, 2010 at 14:11 | #6
  7. August 31st, 2010 at 07:04 | #7
  8. January 4th, 2011 at 21:39 | #8