Flex Chart DataTip Renderer

Here is a quick post with some code to create custom DataTip renderers for Flex charts using Spark components and containers. Below is a screen shot of the custom DataTip and the source code to create it follows.

Main.mxml

<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009" 
					   xmlns:s="library://ns.adobe.com/flex/spark" 
					   xmlns:mx="library://ns.adobe.com/flex/mx" 
					   xmlns:charts="com.thanksmister.charts.*" 
					   width="480" height="340">
	
	<fx:Style source="assets/css/style.css"/>
	
	<fx:Script>
		<![CDATA[
			import mx.charts.CategoryAxis;
			
			private function categoryAxis_labelFunc(item:Object, prevValue:Object, axis:CategoryAxis, categoryItem:Object):String 
			{
				var datNum:Number = Date.parse(item);
				var tempDate:Date = new Date(datNum);
				
				return dateFormatter.format(tempDate).toUpperCase();
			}
		]]>
	</fx:Script>
	
	<fx:Declarations>
		<mx:DateFormatter id="dateFormatter" formatString="MMMM-DD-YYYY" />
	
		<s:XMLListCollection id="dp">
			<s:source>
				<fx:XMLList>
					<quote date="8/1/2007" open="40.29" close="39.58" />
					<quote date="8/2/2007" open="39.4" close="39.52" />
					<quote date="8/3/2007" open="39.47" close="38.75" />
					<quote date="8/6/2007" open="38.71" close="39.38" />
					<quote date="8/7/2007" open="39.08" close="39.42" />
					<quote date="8/8/2007" open="39.61" close="40.23" />
					<quote date="8/9/2007" open="39.9" close="40.75" />
					<quote date="8/10/2007" open="41.3" close="41.06" />
					<quote date="8/13/2007" open="41" close="40.83" />
					<quote date="8/14/2007" open="41.01" close="40.41" />
					<quote date="8/15/2007" open="40.22" close="40.18" />
					<quote date="8/16/2007" open="39.83" close="39.96" />
					<quote date="8/17/2007" open="40.18" close="40.32" />
					<quote date="8/20/2007" open="40.55" close="40.74" />
					<quote date="8/21/2007" open="40.41" close="40.13" />
					<quote date="8/22/2007" open="40.4" close="40.77" />
					<quote date="8/23/2007" open="40.82" close="40.6" />
					<quote date="8/24/2007" open="40.5" close="40.41" />
					<quote date="8/27/2007" open="40.38" close="40.81" />
				</fx:XMLList>
			</s:source>
		</s:XMLListCollection>

		<s:SolidColorStroke id="lineStroke" color="#CCCCCCC" alpha=".2" weight="1"/>
		
	</fx:Declarations>
	
	<s:VGroup width="100%" height="100%" paddingBottom="10" paddingTop="10" paddingLeft="10" paddingRight="10">
		
		<mx:LineChart id="lineChart"
					  showDataTips="true"
					  dataProvider="{dp}"
					  width="100%" gutterRight="10"
					  height="100%" 
					  dataTipRenderer="com.thanksmister.charts.DataTipSkin">
			
			<!-- vertical axis -->
			<mx:verticalAxis>
				<mx:LinearAxis baseAtZero="false" title="Price" />
			</mx:verticalAxis>
			
			<!-- horizontal axis -->
			<mx:horizontalAxis>
				<mx:CategoryAxis id="ca" categoryField="@date" title="Date" labelFunction="categoryAxis_labelFunc" />
			</mx:horizontalAxis>
			
			<!-- horizontal axis renderer -->
			<mx:horizontalAxisRenderers>
				<mx:AxisRenderer axis="{ca}" canDropLabels="true" />
			</mx:horizontalAxisRenderers>
			
			<!-- series -->
			<mx:series>
				<mx:LineSeries yField="@open" form="segment" displayName="Open" />
			</mx:series>
			
			<!-- series filters -->
			<mx:seriesFilters>
				<fx:Array/>
			</mx:seriesFilters>
			
			<!-- assign stroke to grid lines -->
			<mx:backgroundElements>
				<mx:GridLines gridDirection="both" horizontalChangeCount="2" verticalChangeCount="6">
					<mx:horizontalStroke>{lineStroke}</mx:horizontalStroke>
					<mx:verticalStroke>{lineStroke}</mx:verticalStroke>
				</mx:GridLines>
			</mx:backgroundElements>
			
			<mx:annotationElements>
				<fx:Array>
					<charts:RangeSelector id="selectedRange" />
				</fx:Array>
			</mx:annotationElements>
			
		</mx:LineChart>
		
	</s:VGroup>
	
</s:WindowedApplication>

DataTipSkin.mxml

'
<?xml version="1.0" encoding="utf-8"?>
<s:Group  xmlns:fx="http://ns.adobe.com/mxml/2009" 
		 xmlns:s="library://ns.adobe.com/flex/spark"  
		 implements="mx.core.IFlexDisplayObject, mx.core.IDataRenderer"
		 xmlns:mx="library://ns.adobe.com/flex/mx" width="120">
	
	<fx:Script>
		<![CDATA[
			import flashx.textLayout.conversion.TextConverter;
			import flashx.textLayout.elements.TextFlow;
			
			import mx.charts.HitData;
			import mx.charts.series.items.LineSeriesItem;
			
			private var _data:HitData;
			
			[Bindable]
			private var _xValue:String;
			
			[Bindable]
			private var _yValue:String;
			
			[Bindable]
			private var _displayText:TextFlow;
			
			public function get data():Object
			{
				// TODO Auto Generated method stub
				return null;
			}
			
			public function set data(value:Object):void
			{
				// HitData data from chart
				_data = value as HitData;
				
				// The display text used in datatip which comes in HTML format
				_displayText = TextConverter.importToFlow(_data.displayText, TextConverter.TEXT_FIELD_HTML_FORMAT);
				
				// HitData contains a reference to the ChartItem
				var item:LineSeriesItem = _data.chartItem as LineSeriesItem;
				
				// ChartItem xValue and yValue 
				_xValue = String(item.xValue);
				_yValue = String(item.yValue);
			}
		]]>
	</fx:Script>
	
	<fx:Declarations>
		
	</fx:Declarations>
	
	<s:Rect right="0" left="0" bottom="0" top="0">
		<s:filters>
			<s:DropShadowFilter blurX="20" blurY="20" alpha="0.22" distance="5" angle="90" knockout="false" />
		</s:filters>
		<s:fill>
			<s:SolidColor color="0x393939"/>
		</s:fill>    
		<s:stroke>
			<s:SolidColorStroke color="0x1a1a19"  weight="1" alpha=".2" />
		</s:stroke>
	</s:Rect>
	
	<s:VGroup width="100%" height="100%" paddingTop="10" paddingRight="10" paddingBottom="10" paddingLeft="10">
		
		<s:RichEditableText textFlow="{_displayText}" width="100%" textAlign="center" selectable="false" editable="false"/>
		
	</s:VGroup>

</s:Group>

-Mister

Flex Spark Rounded Image and Image Button Controls

I had the pleasure recently of collaborating with Ken Rogers (@pixels4nickels) on a couple of components. I had the need, and probably everyone has at one time or another, of having a image with rounded corners. My other desire was to have an image behave like a button.

So we came up with two components, one is called RoundedImage, this does pretty much what it advertises. It extends the Flex Spark Image control to set a corner radius of the loaded image. The other component is called ImageButton. This extends the Spark Button control and loads an image to create a button with rounded corners, pretty straight forward.

What makes these components nice is that they maintain the aspect ratio of the loaded image by simply setting either a height or width. So if you have a large image and want to scale it proportionally, you just set the width, the height will be scaled maintaining the aspect ratio. If you set both height and width values the loaded image will stretch to fit the dimensions.

Here is a screen shot of the results when setting with, height, or both:

You can take a look at the code for the Rounded Image and the Image Button at the following Gist links:

Michael Ritchie Gist: https://gist.github.com/1627989
Ken Rogers Gist: https://gist.github.com/1625442
Flex FXP File: http://thanksmr.com/examples/imagebuttons/ImageButtons.fxp

Big thanks to Ken for his help working out all the kinks and keeping it simple!

-Mister

Spark Button and ButtonBar with icons and rollover states

The Flash Builder Spark Button control doesn’t come with an icon property out of the box. So you have to extend the Button class and add your own. I created a Spark Skin to add two icons to the Button control, one for the up/disabled state and one for the over/down states.

The Spark ButtonBar control does accommodate an icon, but there is no way to change the icon when the selected index changes. So to change the icon of the selected item, I built a Spark Skin for the ButtonBarButton and the ButtonBar to accomplish the job. Here is the running example of the buttons in action:

IconButtons.swf

The code of the IconButton class and the IconButtonSkin mxml file that accompanies the class:

https://gist.github.com/946886

And the code of the IconButtonBarButton class and the IconButtonBarSkin and IconButtonBarButtonskin mxml

https://gist.github.com/947130

You can also download the IconButtons Flash Builder Project.

-Mr

Spark TextFlow LinkElement Rollover

This is a little example of how to create a skinned rollover popup on a LinkElement object in Flash Builder 4 (Flex 4) within a TextFlow object. This is something I have been waiting for since 2001. The old way to create a rollover action on an HTML link was to create an invisible MovieClip that floats over your hyperlink. Flex Builder 4 greatly simplifies this by adding rollover, mouse, and click events to all hyperlinks within the TextFlow object. This is a HUGE plus

I borrowed the click event action from an example by Peter deHaan posted on his blog Flex Examples. You will need the latest build of the Flex Builder 4 SDK and some helpful instructions on how to install the latest Flex Builder 4 SDK.

Example AIR Application Output

LinkElement Rollover Example

Main.mxml

<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication title="Spark RichEditableText LinkElement Rollover"
        xmlns:fx="http://ns.adobe.com/mxml/2009"
        xmlns:s="library://ns.adobe.com/flex/spark"
        xmlns:mx="library://ns.adobe.com/flex/halo">

    <fx:Script>
        <![CDATA[
            import flashx.textLayout.formats.TextDecoration;
            import mx.managers.PopUpManager;

            import flashx.textLayout.elements.LinkElement;
            import flashx.textLayout.events.FlowElementMouseEvent;
            import mx.controls.Alert;

            private var customToolTip:CustomToolTip;

            protected function clickHandler(evt:FlowElementMouseEvent):void {
                var linkEl:LinkElement = evt.flowElement as LinkElement;
                Alert.show("The '" + linkEl.getFirstLeaf().text + "' link would have gone to " + linkEl.href + " in a " + linkEl.target + " window, but it was cancelled.", "Fun with hyperlinks");
                evt.stopImmediatePropagation();
                evt.preventDefault();
            }

            protected function rollOverHandler(evt:FlowElementMouseEvent):void
            {
                if(!customToolTip){
                    customToolTip = new CustomToolTip();
                    customToolTip.x = this.mouseX - customToolTip.width/2;
                    customToolTip.y = this.mouseY - 40;
                    PopUpManager.addPopUp(customToolTip, this, false);
                }
                customToolTip.text =  LinkElement(evt.flowElement).href;
            }

            protected function rollOutHandler(evt:FlowElementMouseEvent):void
            {
               PopUpManager.removePopUp(customToolTip);
               customToolTip = null;

            }
        ]]>
    </fx:Script>

    <s:RichEditableText id="richEdTxt"  editable="false" selectable="false" focusEnabled="false" horizontalCenter="0" verticalCenter="0">
        <s:textFlow>
            <s:TextFlow>
                <!--
                <s:linkNormalFormat  textDecoration="{TextDecoration.NONE}"/>
                <s:linkHoverFormat textDecoration="{TextDecoration.UNDERLINE}" />
                <s:linkActiveFormat  textDecoration="{TextDecoration.UNDERLINE}" />
                -->
                <s:p>Text that includes a link to <s:a href="http://adobe.com/" target="_blank"
                    rollOver="rollOverHandler(event);" rollOut="rollOutHandler(event);"
                    click="clickHandler(event);">Adobe</s:a>.</s:p>
            </s:TextFlow>
        </s:textFlow>
    </s:RichEditableText>

</s:WindowedApplication>

CustomToolTip

<?xml version="1.0" encoding="utf-8"?>
<s:SkinnableContainer xmlns:fx="http://ns.adobe.com/mxml/2009"
    xmlns:s="library://ns.adobe.com/flex/spark"
    xmlns:mx="library://ns.adobe.com/flex/halo">
    <fx:Script>
        <![CDATA[
            import spark.skins.spark.BorderSkin;

            [Bindable] private var _text:String;

            public function set text(value:String):void
            {
                _text = value;
            }
        ]]>
    </fx:Script>

    <s:Skin minHeight="25">

        <s:Rect id="upFill"
                top="1"
                right="1"
                left="1"
                bottom="1"
                radiusX="10"
                radiusY="10">
            <s:fill>
                <s:LinearGradient rotation="90">
                    <s:GradientEntry color="#222222"  ratio="0" alpha="0.45"/>
                    <s:GradientEntry color="#222222"  ratio="0.45"/>
                    <s:GradientEntry color="#222222"  ratio="0.65"/>
                    <s:GradientEntry color="#222222" ratio=".8"/>
                </s:LinearGradient>
            </s:fill>
            <s:stroke>
                <s:SolidColorStroke color="#222222" weight="0.5"/>
            </s:stroke>
        </s:Rect>

        <s:Rect id="highlightFill"
                top="2"
                right="2"
                left="2"
                bottom="2"
                radiusX="10"
                radiusY="10">
            <s:stroke>
                <s:LinearGradientStroke rotation="90" weight="1">
                    <s:GradientEntry color="#FDFDFD" ratio="0" alpha="0.6"/>
                    <s:GradientEntry color="#FDFDFD" ratio="0.2" alpha="0"/>
                </s:LinearGradientStroke>

            </s:stroke>
        </s:Rect>

        <s:SimpleText id="labelElement" text="{_text}"
                      color="#FFFFFF"
                      right="20"
                      left="20"
                      verticalAlign="middle"
                      horizontalCenter="0"
                      verticalCenter="1"/>

        <s:filters>
            <s:DropShadowFilter color="#999999"
                                blurX="5"
                                blurY="5"
                                angle="90"
                                distance="2"
                                alpha="0.8"/>
        </s:filters>
    </s:Skin>

</s:SkinnableContainer>

The only think I couldn’t get working for this example (the commented out section) was changing the format of the hyperlinks within the TextFlow object.

Source File

Additional Resources

http://labs.adobe.com/technologies/flashbuilder4/
http://livedocs.adobe.com/flex/gumbo/langref/spark/primitives/RichEditableText.html
http://blog.flexexamples.com/2009/08/27/creating-a-linkelement-in-a-spark-richeditabletext-control-in-flex-4/
http://blog.flexexamples.com/2009/10/21/customizing-the-appearance-or-a-hyperlink-in-a-textflow-object-in-flex-4/
http://blog.flexexamples.com/2009/07/13/downloading-and-installing-flex-4-sdk-builds-from-opensource-adobe-com-flash-builder-4-beta-edition/

-Mister