Drag-and-Drop Revisited

Posted in Justin Shacklette on December 14th, 2010 by admin

admin originally posted this on Saturnboy.

It’s time to revisit drag-and-drop in Flex 4. For a long time now, my previous Drag-and-Drop in Flex 4 post has been the most popular post on this blog. I get something like 1,200 pageview/month on that one post. This time around, I’ll try to expand on the thinking that goes on before I write the code to add drag-and-drop functionality to an app.

UX 101

It is impossible to develop an RIA without some basic understanding of interaction design. As they say in one of my favorite movies, "Many men have tried. They tried and failed? They tried and died." So a short UX primer on drag-and-drop goes like this…

Users are typically familiar with drag-and-drop for two basic actions: moving-stuff-around and putting-stuff-into. For example, we move windows around the desktop or move songs around in a playlist or move icons around in a folder. Also, we put stuff into the trash or put a picture into an album or put an attachment into an email. At a fundmental level, users have an understanding of these actions because they mimic the physical world. Performing these actions via a touch interface (rather than a mouse) is even better, but that’s a story for another day.

Given these two basic actions, I’m going to design a sample app that exercises them both. This is what I came up with:

diagram 1

Our sample app has three drag actions. In #1, you can drag to put items from the palette into the list. In #2a and #2b, you can drag items from the list into a target box (that preforms some action). In #3, you can drag items around inside the list to reorder them. I’ll break them down one by one.

#1 — Drag List Item to Another List

Dragging from one list and dropping on another is super easy in Flex 4. Here’s a snippet:

<s:List id="sourceList" dragEnabled="true">
    <s:ArrayList source="['A','B','C','D']" />
</s:List>
 
<s:List id="targetList" dropEnabled="true">
    <s:ArrayList></s:ArrayList>
</s:List>

We set dragEnable=true on the source list and dropEnable=true on the target list. For bi-directional drag-and-drop, just set both attributes true for both lists. In a feat of awesomeness, Flex 4 does everything else for you. You don’t need to listen for any events or write any handlers. And you even get some nice eye candy for free, because the target list glows when you drag an item over it.

Trick 1 — When the drop target is an empty list, you must given it an empty dataProvider. In the above example, I just use an empty ArrayList pair of tags in MXML, but I could just as easily say dataProvider="{new ArrayList()}", or even do everything in Actionscript.

Trick 2 — The source list accepts a drop—if and only if—the item being dropped is compatible with the items in the target dataProvider. For example, you can not drop a Foo object on a list of Bar objects. Setting the target list to have an empty dataProvider (à la Trick 1), accepts anything.

#2 — Drag List Item to UIComponent Target

Again, to use a list as the drag source, we just set dragEnable=true. But this time the target is some non-List UIComponent, so we need to do what we always do to enable drop functionality on a component—namely, attach handlers to the dragEnter and dragDrop events. Here’s a snippet:

<s:List id="sourceList" dragEnabled="true">
    <s:ArrayList source="['A','B','C','D']" />
</s:List>
 
<s:Panel id="targetA" 
         dragEnter="dragEnterHandler(event)"
         dragDrop="dragDropHandler(event)">
</s:Panel>

And here are the handlers:

private function dragEnterHandler(e:DragEvent):void {
    DragManager.acceptDragDrop(e.currentTarget as IUIComponent);
}
private function dragDropHandler(e:DragEvent):void {
    //do something with dropped item here
}

As always, we accept the drop in the dragEnter handler via the static acceptDragDrop() method on the DragManager. And once the object is dropped, we can do whatever we want with it in the dragDrop handler.

#3 — Drag List Item to Reorder Items

Finally, we want to be able to reorder the items in a list. Again, the spark List does all the work for us. We just set both dragEnable=true and dropEnable=true on the list we want to reorder. We must also set the magical dragMoveEnabled=true, which has numerous ramifications. Here is the snippet:

<s:List id="reorderList"
        dragEnabled="true"
        dropEnabled="true"
        dragMoveEnabled="true">
    <s:ArrayList source="['A','B','C','D']" />
</s:List>

The code is trivial, but what’s going on with dragMoveEnabled=true? Well, dragMoveEnable, which defaults to false, controls whether or not an object is moved (aka removed from the source and added to the target) or copied (aka added to the target leaving the source untouched). So to reorder the items in a list, we need the move behavior, thus we set dragMoveEnabled=true.

Trick 3 — If the source list is dragMoveEnabled=true and the target is a non-List UIComponent, you will see some seemingly odd behavior. Every time to drop an object on your component, it will be deleted from the source list. This is because you are only seeing the first half of the move (the item is removed from the source), and not the second half (the item is added to the target) because the target is not a List.

Trick 4 — As an alternative to dragMoveEnabled=true, you can make a call to DragManager.showFeedback(DragManager.MOVE) in the dragEnter handler to control whether or not an object is moved or copied. Do NOT use both showFeedback and dragMoveEnabled at the same time.

Eye candy

What’s a Flex app without eye candy? But with drag-and-drop, I’d say most of the eye candy is more of a UX necessity than anything else. It’s used to indicate potential drop targets, or highlight a target that is ready to receive a drop, etc.

Glow On Enter

With list to list drag-and-drop, we get a glow on the target list for free, which tells the user that the target list is ready to receive the drop. But when dragging to a non-List UIComponent, we need to add the glow ourselves:

private function dragEnterHandler(e:DragEvent):void {
    myGlow.alpha = 1;
    DragManager.acceptDragDrop(e.currentTarget as IUIComponent);
}
private function dragExitHandler(e:DragEvent):void {
    myGlow.alpha = 0;
}
private function dragDropHandler(e:DragEvent):void {
    myGlow.alpha = 0;
    //do something with dropped item here
}

In the dragEnter handler, we turn the glow on, and we turn the glow off on either a dragExit or dragDrop event. And we need to add a simple GlowFilter to the target component:

<s:Panel id="targetA" 
         dragEnter="dragEnterHandler(event)"
         dragDrop="dragDropHandler(event)">
    <s:filters>
        <s:GlowFilter id="myGlow" blurX="16" blurY="16"
                color="#3399ff" alpha="0" />
    </s:filters>
</s:Panel>

No magic, just use MXML to add a GlowFilter to the target component, which in this snippet is a spark Panel component.

Custom Drag Proxy

Customizing the drag proxy (what an item looks like while dragging) is fun, but I covered that previously, so there’s no need to revisit it here. On the other hand, I haven’t discussed how to customizing an item dragged from a spark List yet. It is not only fun, but also quite a bit easier.

Flex 4 makes is super easy to create a custom drag proxy by simply adding a magic dragging state to the List‘s itemRenderer. For this snippet, imagine I have a trivial ItemRenderer that just shows a centered label with black text, but when it’s being dragged I want to turn the text red:

<?xml version="1.0" encoding="utf-8"?>
<s:ItemRenderer ...>
    <s:states>
        <s:State name="normal" />
        <s:State name="hovered" />
        <s:State name="selected" />
        <s:State name="dragging"/>
    </s:states>
 
    <s:Label left="0" right="0" top="0" bottom="0"
            text="{data.label}" textAlign="center" verticalAlign="middle"
            color="#000000"
            color.hovered="#0000ff"
            color.dragging="#ff0000" />
</s:ItemRenderer>

As you can see from the above code, customizing the drag indicator is a huge nothing. Just add the magic dragging state to the list of states, and use Flex 4′s inline state syntax to do whatever you want. In this snippet, we set color.dragging="#ff0000" to turn the text red when its being dragged. The sample app below does quite a few more things to customize the dragged item, so check out that code too.

Custom Drop Indicator

When a user drags an item over a target spark List, a line appears to show the user where the item will be inserted. This line is known as the drop indicator. To change things up, we will make a custom drop indicator that is a dashed line.

Trick 5 — Alas, Flex 4 does not have direct support for dashed strokes, so we need to fake it. The easy way to make a dashed line in Flex 4 is to use BitmapFill. First, we create an image that is a tileable pattern of squares. Then, we make a Rect and set its fill to BitmapFill and fillMode="repeat".

The dropIndicator component is found in the fx:Declarations block of the default spark ListSkin, so we must create a custom List skin with our custom dropIndicator component. Here’s a snippet from our custom skin’s fx:Declarations block:

<fx:Declarations>
    <fx:Component id="dropIndicator">
        <s:Group minWidth="1" minHeight="1" maxWidth="1" maxHeight="1">
            <s:Rect left="0" right="0" top="0" bottom="0">
                <s:fill>
                    <s:BitmapFill source="@Embed('dash.png')"
                            fillMode="repeat" />
                </s:fill>
            </s:Rect>
        </s:Group>
    </fx:Component>
</fx:Declarations>

As described about, we have a Rect with a BitmapFill, and an image dash.png that is a tileable pattern of squares. But the real magic is in the Group wrapper, by setting all of minWidth, maxWidth, minHeight, maxHeight to 1px we ensure we get a one pixel line as our drop indicator independent of the layout applied to our List. This is because HorizontalLayout ignore min and max height, and VerticalLayout ignore min and max width. So if we take a 1px view of our pattern of squares, we see dashes.

Conclusion

Here’s the complete drag-and-drop sample (view source enabled):

Flash is required. Get it here!

Hopefully, I covered the full spectrum of drag-and-drop functionality that the average Flex app (or even above average) is ever likely to need. If you want more, check out the links below, or just leave me a comment.

Useful Links
  • My first post on drag-and-drop in Flex 4 covered a lot of drag proxy stuff.
  • Flex After Dark has a solid drag-and-drop tutorial that is definitely worth reading if you are implementing drag-and-drop functionality in your app.
  • Evtim, aka The Godfather, as I like to call him, has a cool post on customizing both the drag indicator and the drop indicator of the spark List component.
Files

Testing Private Methods in Java

Posted in Justin Shacklette on November 18th, 2010 by admin

admin originally posted this on Saturnboy.

I’ve been writing a lot of Java lately, and a lot of tests. We always write tests, right? Alas, no cool record-and-playback stuff like FlexMonkey or FoneMonkey, just plain old JUnit 4 tests with plently of Hamcrest goodness.

Suddenly, I realized that I really needed to test some private methods. So, a quick google for “testing private methods java” brings up a good article by Bill Venners. He lists all possible options to test private methods:

  1. Don’t test private methods
  2. Give the methods package access
  3. Use a nested test class
  4. Use reflection

Basically, the only real one is #4, use reflection. Bill didn’t give me the exact code I needed, so lots of googling later I realized that the world is filled with opinionated people (like me), and boy do they love to talk about #1. I just wanted some code, not a lecture, so I had to write my own code. Here is that code for anyone else that just wants to test private methods.

Imagine you have a class MyClass and it has a private method, myMethod(). Sorta like this:

public class MyClass {
    private String myMethod(String s) {
        return s;
    }
}

Then you could use reflection to invoke the method like this:

MyClass myClass = new MyClass();
Method method = MyClass.class.getDeclaredMethod("myMethod", String.class);
method.setAccessible(true);
String output = (String) method.invoke(myClass, "some input");

The real magic is setAccessible(true) which allows the private method to be called outside the class. And shazam, I can now test all my private methods. I was really hoping JUnit 4 would provide some additional facilities specifically for testing private methods, but not luck.

A Better Example

Here’s a more complete example. Suppose we have a NovelWriter class that in a feat of API cleanliness only exposes the writeNovel() method. It happens to have a few private utility methods that we’d like to test:

public class NovelWriter {
    public String writeNovel() {
        //...the magic goes here...
        return null;
    }
 
    private String shout(String s) {
        return s.toUpperCase().replaceAll("\\.", "!");
    }
 
    private List<Integer> countLetters(List<String> words) {
        List<Integer> out = new ArrayList<Integer>();
        for (String word : words) {
            out.add( word.replaceAll("[^A-Za-z]+","").length() );
        }
        return out;
    }
}

I won’t get into all the details, but it seems easy to imagine a clean API that has private helper methods. Furthermore, it seems very logical to me to want to bring all methods, both public and private, so I can be sure they are being exercised to the fullest.

Our JUnit 4 + Hamcrest test class:

public class NovelWriterTest {
    public static NovelWriter novelWriter;
 
    @BeforeClass
    public static void beforeClass() {
        novelWriter = new NovelWriter();
    }
 
    @Test
    public void privateShout() throws NoSuchMethodException,
            InvocationTargetException, IllegalAccessException {
 
        String input = "This is magic.";
 
        Method method = NovelWriter.class.getDeclaredMethod("shout", String.class);
        method.setAccessible(true);
        String output = (String) method.invoke(novelWriter, input);
 
        assertThat(output, notNullValue());
        assertThat(output, is("THIS IS MAGIC!"));
    }
 
    @Test
    @SuppressWarnings("unchecked")
    public void privateCountLetters() throws NoSuchMethodException,
            InvocationTargetException, IllegalAccessException {
 
        List<String> input = Arrays.asList("Foo", "Foobar123", "Foo Bar Baz");
 
        Method method = NovelWriter.class.getDeclaredMethod("countLetters", List.class);
        method.setAccessible(true);
        List<Integer> output = (List<Integer>) method.invoke(novelWriter, input);
 
        assertThat(output, notNullValue());
        assertThat(output.size(), is(3));
        assertThat(output, hasItems(3, 6, 9));
    }
}

The nice thing about using the Reflection API like this is that it really doesn’t get too messy. I’m not inspecting anything at runtime, because I know exactly the return type and the types of all the parameters. I’m just invoking the method with known inputs, followed by a simple cast on the output type. And as you can see in the second test above, privateCountLetters(), it’s not a problem to use generics because we’re not doing any inspection only invocation.

Happy testing.

Files
  • novel.tgz – download the complete Eclipse project

To the Mountaintop

Posted in Justin Shacklette on November 10th, 2010 by admin

admin originally posted this on Saturnboy.

Back in December 2009, I had the amazing opportunity to teach for a whole week at Boulder Digital Works. BDW had just started that fall and the first class of 12 students1 was just getting started on a 60-week program.

Teaching was a ton of fun! We wrote some PHP code together and everyone was able to experience a little slice of the joy/hell that is development. We also did a lot of talking, which was fun, too. Since most of the class was non-technical, we didn’t geek out technology stuff, instead we spent a lot of time talking about meta-development. We talked about process, how developers think, how development fits into digital, what it means to actually build something real, etc. I found the whole thing to be very cathartic, having 12 smart people listening to me and participating in a fun discussion was just awesome.

To Be Great

One of the more interesting topics I discussed during class was how to be a great developer. I spend a lot of my time and brainpower thinking about what I can do to be better. That’s the mountaintop, but it’s always a long way away.

Some options for improvement are external but many are internal. For example, I’m always asking myself: Who knows something that I want to know? Who can I model? What can I borrow from those around me? Who can I mentor? Who should I ask to mentor me? What should I learn next? What should I forget? What can I read? Do I need to change my approach? What is today’s definition of value?

I always think my kung-fu is strong, but I’m not a master. So here’s a quick summary of the components of greatness as I see them today. Please feel free to ignore everything as you see fit.

Undying Hunger

If you have the hunger to be better then you’re always on the upslope. And this isn’t the vanilla-level hunger of “Hey, there’s more I could learn.” This is the full on, I’m-starving-more-is-never-enough hunger.

Once upon a time, back when I was a young lad of 10 or so, there was a series of TV commercials asking “Do you see the glass half full or half empty? Blah blah blah” After the hundredth viewing, I turned to my dad and said, “Dad, I see it as not enough! What does that make me?” And in his infinite wisdom, my dad replied, “That makes you a hog!” – which is an apt description without a doubt.

To be great, you need to be a hog.

Effort

Where hunger is internal, effort is external. Where hunger is mostly mental, effort is mostly physical. Effort is all about doing the work, expending the energy, getting really tired, waking up the next day, and doing it all over again. There is an obvious correlation between hunger and effort, but it’s definitely not one of causation. I fully control my decision to turn off the TV and pick up a book or sling some code.

The best thing about effort is that it’s just a switch, and I truly believe that. You can decide for yourself to turn it on any time you want. And with the right carrot, you can even flip the switch on for someone else. In my many years of coaching frisbee, I have repeatedly witnessed the switch being flipped. People can be taught to work hard, but it’s a heck of a lot easier when they are on a team of hard workers.

Unfortunately for me, the effort switch is more of a dimmer switch then an on-off switch. Burning bright and bringing a high level of effort for an extended period of time is hard. Motivation naturally ebbs and flows, and it’s challenging to maintain it. The best solution I’ve found to keep motivation high is to find a partner in crime. When there’s someone else to work with and compete against, it’s much easier to work hard. When I’m on my own, the only trick I have is to maximize variety, some days I work hard by doing some extra reading, some days it’s writing, some days it’s hacking, etc.

Effort in athletics is pretty easy to recognize. It’s spending time in the weight room during the off-season and out-hustling the other guy during the game. Effort in development is a little harder to figure out. Some of my typical outside-of-work adventures include reading (books, blogs), writing (this blog, the occasional article), speaking (local user group), teaching (BDW), and lots of side projects (contribute to open source, silly iPhone apps). There are also quite a few at-work hustle opportunities: you can give a brown bag talk, you can mentor others, and you can be a good mentee (basically you need find someone who knows something you want to know and beg them to teach you). But my favorite thing to do at work is find a like-minded peer and inspire them to work hard. You can do this by co-reading, co-writing, co-speaking, pair programming, etc.

To be great, you must work hard, constantly.

Creative & Pedantic

Unfortunately, no matter how hungry you are or how hard you work, you must also be creative to be a great programmer. Without creativity, you will forever be a cog in the machine. You might be the best cog ever, but you are a cog none the less. In Hackers & Painters2, Paul Graham talks a lot about the creative nature of software construction. Hackers, painters, architects, and writers are all makers in Paul’s world, and he spends quite a bit of prose comparing them.

I really like Paul’s take on the process of construction, and the different disciplines that practice it, but I try to take a more practical view. To me, creativity is nothing more than a measure of one’s ability to escape the box. I see it as just another skill that can be learned, practiced, and improved. Over time, you can learn to recognize the box faster and more fully. The more boxes you see, and solve, the more solution patterns you will remember. Eventually, you will develop a toolbox of escape tactics.

Again, it’s still not enough. No amount of creativity will help you actually implement a solution to any given box. There are a lot of details that are critically important to the construction process. Paul Graham writes, “In both painting and hacking there are some tasks that are terrifyingly ambitious, and others that are comfortingly routine.” Much like Superman has Bizarro, the creativity has pedanticalness (yes that’s a word). Many of the routine tasks in programming are routine to the point of being tedious.

I often liken the pedantic aspect of development to licking envelopes. It’s easy, but yuck! Who actually likes the taste of envelopes? If you don’t do it right, then don’t be surprised if you mail explodes while in transit. In my experience, there are a multitude of development tasks that taste a heck of a lot like envelope glue, but you don’t really have much choice. You can either do them the right way, or watch them explode.

To be great, you must embrace both the creative side of development and the pedantic side.

Ego

The number one, most important, aspect of greatness is ego. The truth of development is that not everything goes your way. And even when it does, it can be exceedingly difficult. For all those times when you are tumbling down the mountain, you need a certain amount of ego to arrest your fall and start upwards again. I’ve actually spent time and effort into stroking my own ego for this simple reason: you learn more when you lose than when you win.

I heard a pretty amazing football stat the other day. In 9 of the last 10 seasons in the NFL, at least one last place division team fought their way to first place in their division the following year. Most recently, the Saints finished 8-8 in 2008 and were in last place in the NFC South. In 2009, they won their division at 13-3 and went on to win the Superbowl. Why? Because you learn more when you lose than when you win. Fact.

The mud is a great teacher. But you must have a certain level of mental fortitude to climb out and get clean again. To me ego is just that – confidence in yourself and your ability, lack of fear, and a basic mental toughness. I’ll go willingly into the mud to take on a difficult problem or learn a new language (or maybe a new API instead of another language). And if I get thrown into the mud unexpectedly, which happens all the time in consulting, there won’t be any fear or panic.

Do I get dirty? Hell yes. Does it stink? Of course. Having confidence in yourself and believing in your abilities doesn’t magically transform the metaphoric mud into flowers, it’s still mud.

To be great, you must be confident and fearless.

Conclusion

It’s hard to know what you don’t know. In fact, it’s really damn hard. But I don’t find it discouraging in the least to know the code I write today is going to suck compared to the code I write tomorrow. That’s called progress.

I feel good when I focus on what I do know. I know I want to be the best dad I can be, the best husband I can be, and the best developer I can be. And today, that means this:

  • undying hunger — more is never enough
  • effort — constantly work hard
  • creative & pedantic — embrace both sides, writing elegant algorithms and head-pounding debugging
  • ego — be confident in yourself

So stay hungry, work your ass off, lick your envelopes, and welcome the mud at every opportunity. Good luck.

“And whenever men and women straighten their backs up, they are going somewhere, because a man can’t ride your back unless it is bent.” — Martin Luther King, Jr 3

Footnotes
  1. I’d like to thank BDW’s First 12 for listening to a crazy opinionated developer. @botvinick, @kygalle, @dasn101, @tetonmarketing, @jefferyjake, @justinmccammon, @rosErin, @JadedSkipping, @hseal, @tegoenfuego, @dviens, @ndubsglobal
  2. Hackers & Painters by Paul Graham
  3. I’ve Been to the Mountaintop by Martin Luther King, Jr. This speech was given on April 3, 1968.

Programmatic Skinning in Flex 4 — an FXG Clock

Posted in Justin Shacklette on November 3rd, 2010 by admin

admin originally posted this on Saturnboy.

I tried to push my Flex 4 skinning skills to the limit, and the result is a fancy clock skin.

clock
(click to see it in action)

The fancy clock skin overlays a very simple clock component. The Clock component is quite underwhelming, but here it is in all of its abbreviated glory:

public class Clock extends SkinnableComponent {
    public var secAngle:Number = 0.0;
    public var minAngle:Number = 0.0;
    public var hourAngle:Number = 0.0;
    private var _timer:Timer;
    private var _tween:GTween;
 
    public function Clock() {
        ...
        _tween = new GTween(this, 2, ...);
        _timer = new Timer(2000,30);
        _timer.addEventListener(TimerEvent.TIMER, tickHandler);
    }
 
    private function tickHandler(e:TimerEvent):void {
        var t:Date = new Date();
 
        var h:Number = t.getHours();
        var m:Number = t.getMinutes() + h * 60;
        var s:Number = t.getSeconds() + m * 60;
        var ms:Number = t.getMilliseconds();
 
        _tween.proxy.secAngle = -90 + s * 6 + ms * 0.006;
        _tween.proxy.minAngle = -90 + s * 0.1;
        _tween.proxy.hourAngle = -90 + (s / 120);
    }
    ...
}

At the core of the Clock component is a single timer. When the timer fires, it uses the current date to compute the angle of the hour hand, minute hand, and second hand. The actual motion of the hands is managed via the awesome proxy functionality in Grant Skinner’s GTween library.

All the interesting FXG action takes place in the skin, so let’s check it out:

<?xml version="1.0" encoding="utf-8"?>
<s:Skin
        xmlns:fx="http://ns.adobe.com/mxml/2009"
        xmlns:s="library://ns.adobe.com/flex/spark"
        creationComplete="complete()">
 
    <fx:Metadata>
        [HostComponent("components.Clock")]
    </fx:Metadata>
 
    <fx:Script>
        <![CDATA[
            private function complete():void {
                ...lots of programmatic FXG here...
            }
        ]]>
    </fx:Script>
 
    ...lots of vanilla FXG here...
 
    <s:Path rotation="{hostComponent.minAngle}" ...
    <s:Path rotation="{hostComponent.hourAngle}" ...
    <s:Path rotation="{hostComponent.secAngle}" ...
</s:Skin>

Getting data to the skin is done via the standard binding to hostComponent. There is no need for the more complicated partAdded() and partRemoved() mechanism, because the Clock component is not intended for general use, unlike my previous components: Terrific TabBar, SuperTextInput, and Drawer.

The skin has a big block of programmatic FXG skinning, done in actionscript code inside the complete() handler which is fired by the creationComplete event. It also has a big block of vanilla MXML declarative FXG skinning, which, most importantly, includes the clock hands and the previously mentioned binding to hostComponent.

By its nature, a clock has a lot of identical elements that are positioned in a circle. The obvious attack is to put the repeated elements in a loop, or set of loops, and go the programmatic route to construct the skin. For example, the long thin ticks along the outer ring are constructed with a double loop. Here’s the relevant snippet of code from inside the complete() handler:

for each (var i:int in [0,30,60,90,120,150,180,210,240,270,300,330]) {
    for each (var j:int in [6,12,18,24]) {
        var p:Path = new Path();
        p.data = "M 174,-0.5 L 184,-0.5 184,0.5 174,0.5 Z";
        p.x = 250;
        p.y = 250;
        p.rotation = i + j;
        p.fill = gray;
        this.addElementAt(p, 1);
    }
}

A new Path element is created and its data property is set. Our tick mark is 10px long, but only 1px wide. With each iteration, a tick mark is created and rotated into place. There are two important tricks in this code that are worth pointing out. First, the rotation origin is at (0,0) for each element, so we must construct our path from -0.5 to 0.5 for it to rotate correctly. Second, we must use addElementAt() to place each element into the proper layer of the drawing because the programmatic FXG code runs after all the vanilla (aka non-programmatic) FXG has been drawn.

Conclusion

No one in their right mind would ever build a clock skin like this. To do this the right way, you’d definitely want to use an image or two to construct the skin. The only reason I went the programmatic FXG skinning route was to verify to myself that it could be done. So, yes it can be done, and I enjoyed the trip.

Files

FlexLayouts.org Launches, Custom Flex 4 Layouts for the Masses

Posted in Justin Shacklette on August 18th, 2010 by admin

admin originally posted this on Saturnboy.

FlexLayouts is an open source library of custom Flex 4 layouts that I’ve veen working on with Gilles Guillemin. Today, I’m happy to announce the first public beta release, v0.5, is available for immediate download. The FlexLayouts Gallery will be your source for layouts, sample code, and documentation. Today’s release includes beta versions of two simple 2D layouts: FlowLayout and SnakeLayout.

Our Dream

We admit v0.5 is not much to look at but imagine a future where a huge number of robust, well tested layouts (both 2D and 3D) exists and is readily available with both sample code and exceptional documentation. You could just slap together some vanilla Flex components, drop in an awesome custom layout (like Gilles’ CoverFlowLayout), and ka-BOOM, you have an awesome custom component that impresses co-workers, bosses, and clients equally. That’s the dream: to take one of the most portable, re-usable pieces of the Flex framework, make a massive library, and empower the next generation of custom components.

Ransom Note

I whipped together a quick sample application using FlowLayout that I’m calling Ransom Note. It pulls together a list of images of letters that look as if they were cut out of a magazine or newspaper and pasted together. It follows one of the most common custom layout patterns which is: List + ItemRenderer + custom layout. The actual implementation uses the lighter weight version: DataGroup + DataRenderer + custom layout, because I don’t need any of the list selection stuff.

Check out the finished product:

» RansomNote (view source enabled)

Files

TerrificTabBar – Another Custom Component in Flex 4

Posted in Justin Shacklette on August 2nd, 2010 by admin

admin originally posted this on Saturnboy.

Another day, another custom component in Flex 4. This time it’s a closeable TabBar that I’m calling TerrificTabBar, because SuperTabBar is taken. This post is basically Part 2 of my previous post on SuperTextInput. And much like that post, TerrificTabBar follows the Enhanced Component Pattern by extending TabBar and adding some new functionality.

Extend TabBar and TabBarButton

I just want to build your average web browser tab in Flex 4, no more, no less. Browser tabs have two cool features: each tab has a close button, and the tabs can be reordered by dragging. Actually, I just lied about the no less part, because I’m going to ignore dragging and focus my attention on the closeable tab part. Please see my previous post on drag-and-drop in Flex 4 if you are interested in the dragging part.

A TabBar is just a bag of tabs, where each individual tab is a button, or a TabBarButton to be more precise. To create a closeable tab, we need to add a new button that can be clicked to close the tab, duh. And this is in addition to the default behavior of clicking the tab to select it. Since we are disciples of The Flex 4 Way of building custom components, we know this means we must extend TabBarButton and add a new Button SkinPart to it:

package components {
    ...
 
    public class TerrificTabBarButton extends ButtonBarButton {
        [SkinPart(required="false")]
        public var closeButton:Button;
 
        private var _closeable:Boolean = true;
 
        public function TerrificTabBarButton() {
            super();
 
            //IMPORTANT: this enables the button's children (aka the close button) to receive mouse events
            this.mouseChildren = true;
        }
 
        [Bindable]
        public function get closeable():Boolean {
            return _closeable;
        }
        public function set closeable(val:Boolean):void {
            if (_closeable != val) {
                _closeable = val;
                closeButton.visible = val;
                labelDisplay.right = (val ? 30 : 14);
            }
        }
 
        private function closeHandler(e:MouseEvent):void {
            dispatchEvent(new TerrificTabBarEvent(TerrificTabBarEvent.CLOSE_TAB, itemIndex, true));
        }
 
        override protected function partAdded(partName:String, instance:Object):void {
            super.partAdded(partName, instance);
 
            if (instance == closeButton) {
                closeButton.addEventListener(MouseEvent.CLICK, closeHandler);
                closeButton.visible = closeable;
            } else if (instance == labelDisplay) {
                labelDisplay.right = (closeable ? 30 : 14);
            }
        }
 
        override protected function partRemoved(partName:String, instance:Object):void {
            super.partRemoved(partName, instance);
 
            if (instance == closeButton) {
                closeButton.removeEventListener(MouseEvent.CLICK, closeHandler);
            }
        }
    }
}

First, we have the closeButton SkinPart and it’s closeHandler() event handler. Wiring the handler function to the button is done in the partAdded() override, and un-wiring in the partRemoved() override. The handler just bubbles a closeTab custom event which will be handled by the parent TabBar. Next, we use the closeable property to manage the close button’s visibility. Finally, an additional button visibility check is necessary in partAdded() to set the initial visibility.

There are two other interesting pieces of code. One, we must set mouseChildren = true in the constructor to enable our button within a button to receive and respond to mouse events. Two, in a somewhat hackish fashion, I add or remove padding from the tab’s label when the close button is present or not.

Now that we have a nice close button on the individual tabs, we extend TabBar to make a pretty API for managing tabs and their new functionality. Here is an abbreviated TerrificTabBar component showing the relevant public methods:

package components {
    ...
 
    public class TerrificTabBar extends TabBar {
        public function TerrificTabBar() {
            super();
        }
 
        public function setCloseableTab(index:int, value:Boolean):void {
            if (index >= 0 && index < dataGroup.numElements) {
                var btn:TerrificTabBarButton = dataGroup.getElementAt(index) as TerrificTabBarButton;
                btn.closeable = value;
            }
        }
        public function getCloseableTab(index:int):Boolean {
            if (index >= 0 && index < dataGroup.numElements) {
                var btn:TerrificTabBarButton = dataGroup.getElementAt(index) as TerrificTabBarButton;
                return btn.closeable;
            }
            return false;
        }
 
        private function closeHandler(e:TerrificTabBarEvent):void {
            closeTab(e.index, this.selectedIndex);
        }
 
        public function closeTab(closedTab:int, selectedTab:int):void {
            ...
        }
 
        protected override function partAdded(partName:String, instance:Object):void {
            super.partAdded(partName, instance);
 
            if (instance == dataGroup) {
                dataGroup.addEventListener(TerrificTabBarEvent.CLOSE_TAB, closeHandler);
            }
        }
 
        protected override function partRemoved(partName:String, instance:Object):void {
            super.partRemoved(partName, instance);
 
            if (instance == dataGroup) {
                dataGroup.removeEventListener(TerrificTabBarEvent.CLOSE_TAB, closeHandler);
            }
        }
    }
}

There is a public getter and setter for toggling the close-ability of individual tabs, setCloseableTab() and getCloseableTab(). And also a public method for closing a tab, closeTab(), which can be used to force close an un-closeable tab. Additionally, closeTab() is used internally to close a tab when its close button is clicked. As is standard, the wiring and un-wiring of the closeHandler() handler function is done in the partAdded() and partRemoved() overrides.

Very vanilla, but there is one interesting part worth mentioning. I made the decision to attach the listener to the dataGroup property of TabBar, this is the default container of individual tabs (aka TerrificTabBarButtons). Yes, I could just as easily have attached the handler directly to TabBar in the constructor and used weak references. But after having built enough custom Flex 4 components, it feels funny to me to do any event wiring outside of partAdded() and partRemoved(). Thus, I made the decision to attached to dataGroup. Without doing a bunch of testing (which I haven’t done), I can’t say if this is better or worse from a memory or performance perspective, I just like the way this code looks better.

Conclusion

I put a pretty skin on everything using some of my favorite drawing tricks, but that’s the subject of a future post. Some brief highlights include:

  • I used CSS to wire my components to my skins, so I minimize the use of skinClass attributes for better decoupling
  • I used a big negative gap in TerrificTabBarSkin to make the tabs overlap
  • I set a maxWidth and a minWidth on the individual tabs in TerrificTabBarButtonSkin, which in combination with maxDisplayedLines="1" on the Label, make the tabs truncate with ... when they get too big
  • The curved tab shape is drawn with a Path, but I wrap it with a Group and set the Group‘s scale-9 grid (scaleGridLeft, scaleGridTop, etc.) so it expands and contracts correctly
  • The close button’s x is drawn as a + around the coordinate origin, also known as (0,0), and then rotated 45 degrees

Here’s the finished product (view source enabled):

Flash is required. Get it here!

Use it and enjoy.

UPDATE: Jon Rose has written an excellent FlexMonkey Deep Dive article on how to bring a custom component under automation. He uses TerrificTabBar as an example. Thanks Jon.

Files

SuperTextInput – Building a Custom Component in Flex 4

Posted in Justin Shacklette on July 22nd, 2010 by admin

admin originally posted this on Saturnboy.

I’ve been building a lot of Flex 4 custom components lately, including a sliding drawer, a multiple content area container, and now SuperTextInput. Nor will this be that last, because I think I have a few more in me (update: see TerrificTabBar). I thought it would be useful to spend some time in the details, explaining The Flex 4 Way and how I try to walk the path.

SuperTextInput is a prompting, clearable TextInput extension in Flex 4. It’s just an enhanced version of the default TextInput control, and as such, it follows a fairly standard pattern of custom component creation.

Enhanced Component Pattern

It’s almost too stupid to call this a pattern, but it’s so common in custom component creation that I’ll run with it. Also, I’ve found it to be worthwhile to distinguish between adding new functionality to a component already present in the framework (aka an enhanced component) versus creating a truly custom component.

The enhanced component pattern is just two simple steps:

  1. Extend – extend some default component and add some new functionality
  2. Skin – make it look good

In my version of reality, these steps carry equal weight, because almost all worthwhile functionality in Flex touches the UI in some fashion, so the design and UX (the look-and-feel, it’s usability, the integration into the rest of the app, etc.) are critical. Don’t forget or skimp on step #2 because it’s all the client, team, customer ever sees.

A Prompting TextInput

Since SuperTextInput has two new pieces of functionality (the prompt and the clear button), I’ll split them apart, and consider each part separately. First, the prompt is merely the text you see when the TextInput is empty. It often becomes a space saving label, because it can be used to tell the user what goes into the TextInput without costing the UI any screen real estate.

Thinking more about the prompt, we want the prompt text to be visible initially, but it should disappear when the user clicks (or tabs) to the control, and only returns when the control loses focus and is still empty. So this tells us that we need to communicate both the prompt text and it’s visibility to our skin. The prompt text can just be a simple Label SkinPart, but it’s visibility is complicated enough that it makes sense to add a new prompting SkinState.

Here’s a functioning PromptingTextInput custom component (which is simply the prompting code lifted from SuperTextInput.as):

package components {
    import flash.events.FocusEvent;
    import mx.events.FlexEvent;
    import spark.components.Label;
    import spark.components.TextInput;
    import spark.events.TextOperationEvent;
 
    [SkinState("prompting")]
    public class PromptingTextInput extends TextInput {
 
        [SkinPart(required="false")]
        public var promptDisplay:Label;
 
        private var _prompt:String = '';
        private var _focused:Boolean = false;
 
        public function PromptingTextInput() {
            super();
 
            //watch for programmatic changes to text property
            this.addEventListener(FlexEvent.VALUE_COMMIT, textChangedHandler, false, 0, true);
 
            //watch for user changes (aka typing) to text property
            this.addEventListener(TextOperationEvent.CHANGE, textChangedHandler, false, 0, true);
        }
 
        [Bindable]
        public function get prompt():String {
            return _prompt;
        }
        public function set prompt(value:String):void {
            if (_prompt != value) {
                _prompt = value;
                if (promptDisplay != null) {
                    promptDisplay.text = value;
                }
            }
        }
 
        private function textChangedHandler(e:Event):void {
            invalidateSkinState();
        }
 
        override protected function focusInHandler(event:FocusEvent):void {
            super.focusInHandler(event);
            _focused = true;
            invalidateSkinState();
        }
        override protected function focusOutHandler(event:FocusEvent):void {
            super.focusOutHandler(event);
            _focused = false;
            invalidateSkinState();
        }
 
        override protected function partAdded(partName:String, instance:Object):void {
            super.partAdded(partName, instance);
 
            if (instance == promptDisplay) {
                promptDisplay.text = prompt;
            }
        }
 
        override protected function getCurrentSkinState():String {
            if (prompt.length > 0 && text.length == 0 && !_focused) {
                return 'prompting';
            }
            return super.getCurrentSkinState();
        }
    }
}

In addition to the promptDisplay SkinPart and the new prompting SkinState, there is a lot of other stuff going on in the above code. First, as is typical with data-driven SkinParts, we back the promptDisplay with a good old prompt property. The net is the fairly common pattern of: check if the SkinPart is not null, then do something to it. So in the prompt setter, we assign the incoming value to the private _prompt variable, then check if promptDisplay is available and if yes, set it’s text property. The setter does the job of updating the prompt, but only once everything is happily running. In order to get the data to the skin initially, we must use the partAdded() override to pass the local prompt to the promptDisplay‘s text property. And that’s it for the prompt text.

The prompt visibility part requires lots of event watching, and also SkinState stuff because we made the choice to push visibility via the prompting SkinState. First, we wire up both the programmatic text change events and the user text change events to a handler, textChangedHandler(), that does nothing more than invalidate the state. TextInput change events are a little wacky, but the code works fine. Next, instead of wiring the focus events to another handler (as seen in this prompting TextInput component by Andy McIntosh), we simply override the protected handlers in the parent and add our focus-tracking logic directly. Finally, we override getCurrentSkinState() to do the work of figuring out whether or not the prompt should be displayed.

A skin for PromptingTextInput is now trivial because our component does the work of pushing the important information to the skin. If we ignore all the pretty stuff, the skin is very simple:

<?xml version="1.0" encoding="utf-8"?>
<s:SparkSkin ...>
    ...
    <s:states>
        <s:State name="normal"/>
        <s:State name="prompting"/>
        <s:State name="disabled"/>
    </s:states>
 
    <s:RichEditableText id="textDisplay" ... />
    <s:Label id="promptDisplay" includeIn="prompting" ... />
</s:SparkSkin>

We add the prompting State to the list of states and also add the promptDisplay Label component. By using the standard inline state syntax, includeIn="prompting" our Label is shown only in the prompting state.

A Clearable TextInput

The second piece of SuperTextInput functionality is the clear button. The clear button appears when the TextInput has a value, and when clicked, it clears that value (which re-displays the prompt). Again, there are two pieces of information the need to be communicated to the skin to create the clear button functionality: the button itself and it’s visibility. In this case, since the visibility is so simple (on if TextInput has a value, otherwise off), we’ll just punt and manage it directly in the component. Therefore, the only a Button SkinPart for the clear button will be pushed to the skin.

Here’s a functioning ClearableTextInput custom component (which is simply the clear button code lifted from SuperTextInput.as):

package components {
    import flash.events.Event;
    import flash.events.MouseEvent;
    import mx.events.FlexEvent;
    import spark.components.Button;
    import spark.components.TextInput;
    import spark.events.TextOperationEvent;
 
    public class ClearableTextInput extends TextInput {
 
        [SkinPart(required="false")]
        public var clearButton:Button;
 
        public function ClearableTextInput() {
            super();
 
            //watch for programmatic changes to text property
            this.addEventListener(FlexEvent.VALUE_COMMIT, textChangedHandler, false, 0, true);
 
            //watch for user changes (aka typing) to text property
            this.addEventListener(TextOperationEvent.CHANGE, textChangedHandler, false, 0, true);
        }
 
        private function textChangedHandler(e:Event):void {
            if (clearButton) {
                clearButton.visible = (text.length > 0);
            }
        }
 
        private function clearClick(e:MouseEvent):void {
            text = '';
        }
 
        override protected function partAdded(partName:String, instance:Object):void {
            super.partAdded(partName, instance);
 
            if (instance == clearButton) {
                clearButton.addEventListener(MouseEvent.CLICK, clearClick);
                clearButton.visible = (text != null && text.length > 0);
            }
        }
 
        override protected function partRemoved(partName:String, instance:Object):void {
            super.partRemoved(partName, instance);
 
            if (instance == clearButton) {
                clearButton.removeEventListener(MouseEvent.CLICK, clearClick);
            }
        }
    }
}

After the PromptingTextInput, the ClearableTextInput is a little more straightforward. First, we have the clearButton SkinPart and it’s clearClick() event handler. Wiring the handler function to the button is done in the partAdded() override, and un-wiring in the partRemoved() override. Next, button visibility is managed by watching for both programmatic text change events and user text change events. The handler, textChangedHandler(), sets the button as visible when the control has text in it.

As I mentioned above, I decided against pushing the clearButton‘s visibility down to the skin via a SkinState, and instead chose to manage it inside the component by setting clearButton.visible directly. I tend to favor the SkinState method when more than one thing needs to change in the skin or if I need advanced visuals (like transitions). If I need to do just one thing and I don’t care about visuals, I’ll do it inside the component. The two examples here aren’t the best to illustrate the two options, but that’s my general thought process when building a custom component and custom skin.

A skin for ClearingTextInput is super trivial. Again, ignoring all the pretty stuff, the skin is:

<?xml version="1.0" encoding="utf-8"?>
<s:SparkSkin ...>
    ...
    <s:states>
        <s:State name="normal"/>
        <s:State name="disabled"/>
    </s:states>
 
    <s:RichEditableText id="textDisplay" ... />
    <s:Button id="clearButton" ... />
</s:SparkSkin>

Just add the clearButton Button and position it.

Fusion, Glorious Fusion

The fusion process of creating SuperTextInput from PromptingTextInput and ClearableTextInput is nothing more than copy and paste. SuperTextInput has lots of uses, but my favorite is to use it to capture text input to filter a list. It also works great as a search box, or in any smart form UI. Enjoy.

Here’s the finished product showing all three custom components skinned and ready for action (view source enabled):

Flash is required. Get it here!
Files

Building Flex 4 Containers with Multiple Content Areas

Posted in Justin Shacklette on July 15th, 2010 by admin

admin originally posted this on Saturnboy.

Back in the days of Flex 3, if you wanted multiple content areas in your main application, you’d need to arrange some set of containers (Canvas, HBox, VBox) in the app and fill them with content. It was just your basic Flex 3 development process. The danger, of course, is that you are mixing content with presentation, aka bad separation of concerns. Today, with the power of Flex 4 skins, we can avoid this issue by moving the presentation layer into a skin (or set of skins). And thus, we can do a much better job achieving a happy level of separation of concerns.

The Flex 3 Way

To give a concrete example, I’ll build a blog layout (yes, another blog layout) with a header, footer, sidebar, and main content areas. But before we get started, let’s review the old Flex 3 way:

<mx:HBox id="header">
    <mx:Image source="@Embed('assets/logo.png')" />
</mx:HBox>
 
<mx:Canvas id="body" width="800">
    <mx:Text text="main content" width="600" />
 
    <mx:VBox id="sidebar" x="600" width="200">
        <mx:Text text="Sidebar" />
        <mx:Text text="sidebar content" width="100%" />
    </mx:VBox>
</mx:Canvas>
 
<mx:VBox id="footer">
    <mx:Text text="2010 saturnboy" styleName="footer" />
</mx:VBox>

The above code comes from a previous post, Designing in Flex 3, but has been modified to make sense here. You’ve got you basic blog design: a box for the header, footer, and body, where body is subsequently is divided into a main content area and a sidebar.

The 3-in-4 Way, aka The Wrong Way

The unfortunate next step in a Flex developer’s evolution is what I like to call the Flex 3-in-4 way. This is a the way of neanderthals, which is to say, it is an evolutionary dead end. If you ever have the bad luck to see 3-in-4 code, you can be sure you are dealing with a novice Flex 4 developer. In general, the 3-in-4 way consists of making the simple transcription: CanvasGroup, HBoxHGroup, VBoxVGroup. But the most damning tipoff of a 3-in-4 developer is the assertion that one is now a Flex 4 developer and the learning curve wasn’t all that bad. While I do think Flex 4 is more of an evolutionary release than a revolutionary release, it’s different enough. And it is particularly different on the design side of the framework, how it handles skins, layout, etc.

If we just transcribe the above example, we get some classic 3-in-4 code:

<s:HGroup id="header">
    <s:Label text="Multi Content Area Example" styleName="header" />
</s:HGroup>
 
<s:Group id="body" width="800">
    <s:Label text="main content" width="600" />
 
    <s:VGroup x="600" width="200" styleName="sidebarBox">
        <s:Label text="Sidebar" styleName="title" />
        <s:Label text="sidebar content" styleName="sidebar" />
    </s:VGroup>
</s:Group>
 
<s:VGroup id="footer">
    <s:Label text="2010 saturnboy" styleName="footer" />
</s:VGroup>

*Barf*, please not do this. This code has all the same issues as the Flex 3 code in the first example, and moreover it is a slap in the face of The Flex 4 Way and all of its improvements.

The Flex 4 Way

Yes, there is a Flex 4 Way and it looks like this.

First, we rewrite the main app using a custom container. Ignoring the specifics of the custom container for a moment, here is the re-written main app (minus some clutter):

<containers:headerContent>
    <s:Label text="Multi Content Area Example" styleName="header" />
</containers:headerContent>
 
<containers:sidebarContent>
    <s:RichText left="0" right="0" styleName="sidebar">
        <s:content>
            <s:p fontSize="20">Sidebar</s:p>
            <s:p>sidebar content</s:p>
        </s:content>
    </s:RichText>
</containers:sidebarContent>
 
<containers:footerContent>
    <s:Label text="2010 saturnboy" styleName="footer" />
</containers:footerContent>
 
<s:RichText left="0" right="0">
    <s:content>
        <s:p fontSize="30">Content</s:p>
        <s:p>main content</s:p>
    </s:content>
</s:RichText>

As you can see, the main app is now a nice set of semantic buckets, one for each of the content areas. Header stuff goes in the headerContent bucket, footer stuff goes in the footerContent bucket, etc.

Building a Multi Content Area Container

Second, we need to create a custom container with the nice set of semantic buckets used in the above code. This is achieved by following a straightforward formula:

  1. Extend SkinnableContainer – Extend SkinnableContainer or some child class. In our sample app, our custom container extends Application (which extends SkinnableContainer).
  2. Add Buckets – add some content buckets (in the form of xxxContent) as Arrays. These become the MXML tags used to bucket components together. Each content bucket has a public getter, but most importantly a public setter that accepts an incoming Array of IVisualElements and uses the magical mxmlContent property to assign it to the associated SkinPart.
  3. Add SkinParts – add some matching SkinParts (in the form of xxxGroup) as spark Groups. There are used in the custom skin to display the content. Also, I usually set required="false" to make everything optional.
  4. Add partAdded() & partRemoved() – override the new Flex 4 skinning lifecycle methods to wire the incoming content to the outgoing SkinPart.

The custom component code is actually easier to follow then the description. Here is a custom container with only one additional content bucket, sidebarContent, and its matching SkinPart, sidebarGroup:

package containers {
    import spark.components.Group;
    import spark.components.Application;
 
    public class MainApp extends Application {
        [SkinPart(required="false")]
        public var sidebarGroup:Group;
 
        private var _sidebarContent:Array = [];
 
        public function MainApp() {
            super();
        }
 
        [ArrayElementType("mx.core.IVisualElement")]
        public function get sidebarContent():Array {
            return _sidebarContent;
        }
        public function set sidebarContent(value:Array):void {
            _sidebarContent = value;
            if (sidebarGroup) {
                sidebarGroup.mxmlContent = value;
            }
        }
 
        override protected function partAdded(partName:String, instance:Object):void {
            super.partAdded(partName, instance);
 
            if (instance == sidebarGroup) {
                sidebarGroup.mxmlContent = _sidebarContent;
            }
        }
 
        override protected function partRemoved(partName:String, instance:Object):void {
            super.partRemoved(partName, instance);
 
            if (instance == sidebarGroup) {
                sidebarGroup.mxmlContent = null;
            }
        }
    }
}

Following the four steps: we extend Application, have a sidebarContent bucket and its associated sidebarGroup SkinPart, and override partAdded() and partRemoved() to wire everything together.

Skinning a Multi Content Area Container

Skinning in Flex 4 is awesome, and like everyone says, it’s easily one of the best new features in the framework. While I find the skinning process fairly straightforward, I would never call it trivial, mostly due to the depth and flexibility of the skinning system.

We need a custom skin for our custom multi content area component. This is probably the 10% case for skinning, but it’s also the coolest. In my experience, an average Flex 4 app has many Button skins (like 10 or even 20), a few default component skins (skins for List, DropDownList, TextInput, etc.), and maybe only two or three skins for custom components.

The skin itself is nothing special. To display our custom component’s SkinParts, we simply include a Group with the matching id attribute. For example, our skin will include a <s:Group id="sidebarGroup" /> to display the sidebarGroup SkinPart. Just rinse, wash, repeat, to add all of our custom content areas in the container to the skin.

Here is a trivial skin:

<s:Skin ... >
    <fx:Metadata>
        [HostComponent("containers.MainApp")]
    </fx:Metadata> 
 
    <s:states>
        <s:State name="normal" />
        <s:State name="disabled" />
    </s:states>
 
    <s:VGroup left="40" right="40" top="40" bottom="40" gap="20">
        <s:Group id="headerGroup" width="100%" />
        <s:Group id="contentGroup" width="100%" />
        <s:Group id="sidebarGroup" width="100%" />
        <s:Group id="footerGroup" width="100%" />
    </s:VGroup>
</s:Skin>

In this trivial skin, we just shove all the content groups (including SkinnableContainer‘s default Group, contentGroup) into a VGroup. Also note, we correctly set HostComponent to our custom container. If you are thinking, "Hey, this skin looks similar to the Flex 3 and 3-in-4 example code, just minus the content" that’s exactly the point.

Lastly, we wire out skin to our custom component via CSS:

containers|MainApp {
    skinClass:ClassReference('skins.TrivialAppSkin');
}

Using skinClass to wire a skin to a component is so 2009. The sample app has its CSS inline, but in any real app I’ll always put this in an external file.

Conclusion

After this, there’s really not much more to say. You can certainly create a more complicated arrangement of the multiple content areas by making a more complicated skin. I’ve done exactly this in the final sample, which includes three different skins and a skin switcher (click 1, 2, or 3 to switch skins).

» view MultiConentArea sample (view source enabled)

Files

Drawer Component in Flex 4

Posted in Justin Shacklette on June 1st, 2010 by admin

admin originally posted this on Saturnboy.

I needed a good way to have a large settings panel with a minimal visual impact. The obvious answer is to hide or minimize or collapse the settings panel when not in use. I thought about using Flexlib‘s WindowShade component (which I’ve dicussed in detail in Styling Flexlib’s WindowShade), but why reuse something when you can reinvent the wheel? Plus, it always helps to hone my Flex 4 custom component kung-fu. So, I chose to implement a simple sliding drawer component from scratch as a Flex 4 component.

The Drawer component is a vanilla container (it actually extends SkinnableContainer), so it will happily take any spark component for its children. Before I dive into my implementation, let’s check out the finished drawer component in action (view source enabled):

Flash is required. Get it here!

Just click on the handle to open and close the drawer.

Extending SkinnableContainer

The Drawer component itself, is just pure AS3, but the demo above uses a few MXML skins to achieve the desired look-and-feel. This is a pretty standard pattern that I see during Flex 4 development, so expect it when you write your own custom components.

We’ll review the component implementation in two steps. First, we focus on the skin state management aspect of the drawer. Here’s the relevant code (taken from Drawer.as):

[SkinState("opened")]
public class Drawer extends SkinnableContainer {
    private var _opened:Boolean = false;
 
    public function get opened():Boolean {
        return _opened;
    }
 
    public function set opened(value:Boolean):void {
        if (_opened != value) {
            _opened = value;
            invalidateSkinState();
        }
    }
 
    override protected function getCurrentSkinState():String {
        return (opened ? 'opened' : super.getCurrentSkinState());
    }
    ...
}

The Drawer component can either be closed (the default) or opened. To model theses states, we use the normal state from the superclass to represent the closed drawer, and add a new opened SkinState to represent the opened drawer. We just expose a simple opened boolean property with a custom getter and setter, and then override the getCurrentSkinState() method. It’s important to remember the states we are talking about are skin states, and not component states (see Flex 4 Component States vs. Skin States for the difference).

Second, we focus on the action of opening and closing the drawer. Here’s the relevant code (taken from Drawer.as):

public class Drawer extends SkinnableContainer {
    ...
    [SkinPart(required="false")]
    public var openButton:Button;
 
    private function clickHandler(event:MouseEvent):void {
        opened = !opened;
    }
 
    override protected function partAdded(partName:String, instance:Object):void {
        super.partAdded(partName, instance);
        if (instance == openButton) {
            openButton.addEventListener(MouseEvent.CLICK, clickHandler);
        }
    }
 
    override protected function partRemoved(partName:String, instance:Object):void {
        super.partRemoved(partName, instance);
        if (instance == openButton) {
            openButton.removeEventListener(MouseEvent.CLICK, clickHandler);
        }
    }
}

The Drawer component includes a simple spark button, as an optional SkinPart, that is used to initiate the state change. The partAdded() and partRemoved() methods are overridden to manage and adding and removing of the button’s click event handler. And lastly, the clickHandler() method flips between skin states by toggling the opened boolean property.

Usage

Using the Drawer is the same as any container. In MXML, just put any child components you want between the container’s open and close tags:

<containers:Drawer ... skinClass="skins.DrawerSkin">
    <!-- components go here -->
</containers:Drawer>

Here we also apply the DrawerSkin to our container.

Skins

The DrawerSkin is responsible for creating the desired look-and-feel and generally making the Drawer component look cool. Here are the interesting parts of the skin:

<s:Skin ...>
 
    <fx:Metadata>
        [HostComponent("containers.Drawer")]
    </fx:Metadata>
 
    <s:states>
        <s:State name="normal" />
        <s:State name="opened" />
        <s:State name="disabled" />
    </s:states>
 
   ...
 
    <s:Button id="openButton" ...
            skinClass="skins.DrawerOpenButtonSkin"
            skinClass.opened="skins.DrawerCloseButtonSkin" />
 
    <s:Group id="contentGroup" ... includeIn="opened" />
</s:Skin>

The DrawerSkin has three skin states, normal and disabled are inherited from SkinnableContainer, but opened is our custom skin state. The skin also includes the optional openButton SkinPart, which itself uses two custom buttons skins, one for the open drawer and one for the closed drawer. Lastly, note that the container’s content is only displayed when the skin is in the opened state via the newfangled inline state syntax: includeIn="opened".

Files

Metrics and the AIR Install Badge

Posted in FlexMonkey, Justin Shacklette on May 19th, 2010 by admin

admin originally posted this on Saturnboy.

The AIR Install Badge is a very handy little flash application for delivering AIR applications to your users via the web. The badge allows your users to download and install both your application and the Adobe AIR runtime. Additionally, the install badge will automatically prompt users to upgrade if a previously installed version is detected. At Gorilla Logic, we use the AIR Install Badge on the FlexMonkey download page (free registration required).

Alas, flash is opaque to analytics. We have no idea what our users are doing inside the AIR Install Badge application. Are they installing? Or upgrading? No problem, we just need to write some code…

The Code

Using flash’s ExternalInterface, we can manually push the data out of flash and into javascript. Once we have the data in javascript, we have total control. One option is to use google analytics to store our badge data. In the case of FlexMonkey, we send the badge data along with the user’s credentials to our CRM platform, SalesForce.com.

Step 1: First, open AIRInstallBadge.as and add this to the top:

import flash.external.ExternalInterface;

Step 2: Next, add the ExternalInterface call to the top of the handleActinClick() function in AIRInstallBadge.as:

protected function handleActionClick(evt:MouseEvent):void {
    if (action == 'install' || action == 'upgrade') {
        //send data to js
        ExternalInterface.call('badgeJS',action);
    }
    ...
}

Since I only care about the install or upgrade actions, I’ll only send those out to javascript. Re-compile the badge and deploy.

Step 3: Last, add the badgeJS() javascript callback to the page containing the badge and do whatever you want with the incoming badge data:

function badgeJS(action) {
    //do metrics here...
    alert('badge action=' + action);
}

Conclusion

With an hour of effort, and a very small amount of code, we’ve managed to get the useful metrics of installs and upgrades out of the AIR Install Badge and into our analytics engine of choice. A job well done.