Thursday, December 11, 2008

Add and remove lineseries - Flex LineChart

This took me some time to figure out so I thought I would post about it.

Main objective is to toggle on and off lineseries based on user input. Usually this would mean editing the dataprovider, tricky thing here is that I need the color of each line to always be the same based on data, so instead I leave the dataprovider alone and actually add and remove LineSeries to/from the LineChart. This probably isn't the best way to do this, but hey, it works, and it may help someone else out.

Here we go...

First lets start with the MXML:
Here I have a Panel with a LineChart and a Legend inside it, as well as 3 Toggle Buttons. You will notice that there are no series inside the LineChart.

<mx:Panel title="LineChart Example" height="500" width="100%" layout="vertical">

<mx:HBox id="choices" horizontalGap="0">
<mx:Button id="Profit" label="Profit" toggle="true" click="updateChart(event)"/>
<mx:Button id="Expenses" label="Expenses" toggle="true" click="updateChart(event)"/>
<mx:Button id="Amount" label="Amount" toggle="true" click="updateChart(event)"/>
</mx:HBox>

<mx:LineChart id="linechart" height="80%" width="100%" showDataTips="true" dataProvider="{expensesAC}" creationComplete="init()">

<mx:horizontalAxis>
<mx:CategoryAxis categoryField="Month"/>
</mx:horizontalAxis>

</mx:LineChart>

<mx:Legend dataProvider="{linechart}" labelPlacement="right" verticalGap="2" direction="vertical"/>

</mx:Panel>


And here is the DataProvider:

[Bindable]
private var expensesAC:ArrayCollection = new ArrayCollection( [
{ Month: "Jan", Profit: 2000, Expenses: 1500, Amount: 450 },
{ Month: "Feb", Profit: 1000, Expenses: 200, Amount: 600 },
{ Month: "Mar", Profit: 1500, Expenses: 500, Amount: 300 },
{ Month: "Apr", Profit: 1800, Expenses: 1200, Amount: 900 },
{ Month: "May", Profit: 2400, Expenses: 575, Amount: 500 } ]);


For ActionScript, lets start with the UpdateChart method:

This method looks to see if the button you just pressed is selected (toggle on) or not. If the toggle is on that means the user is adding the line, if the toggle is off that means the user is removing the line, and this method calls the appropriate method.

private function updateChart(evt:Event):void
{
if ( evt.currentTarget.selected == true )
{
addToChart(evt.currentTarget.label);
} else {
removeFromChart(evt.currentTarget.label);
}
}


Here is the addToChart method. This method creates a new LineSeries and adds it to the chart. Note that the LineSeries DataProvider is the same of the LineChart and the data you're referring to (yField) should already be present in the DataProvider.

private function addToChart(item:String):void
{
var newLS:LineSeries = new LineSeries();
var newStroke:Stroke = new Stroke();
newStroke.color = strokeColor(item);

newLS.yField = item;
newLS.displayName = item;
newLS.setStyle('form','curve');
newLS.dataProvider = expensesAC;
newLS.setStyle('lineStroke',newStroke);
var tmp:Array = linechart.series;
tmp.push(newLS);
linechart.series = tmp;
linechart.invalidateSeriesStyles();
}

You may have noticed the line newLS.setStyle('lineStroke',newStroke);. This refers to the following method, all it does is assigns a color based on the button selected.

private function strokeColor(item:String):uint
{
switch (item)
{
case 'Amount':
return 0x0000FF;
break;
case 'Profit':
return 0xFF0000;
break;
case 'Expenses':
return 0x00FF00;
break;
default:
return 0x000000;
break;
}
}


And finally here is the remove method.

private function removeFromChart(item:String):void
{
var objToRemoveIndex:int;
for ( var i:int = 0; i < linechart.series.length; i++ )
{
if ( linechart.series[i].yField == item )
{
objToRemoveIndex = i;
}
}

var tmp:Array = linechart.series;
tmp.splice(objToRemoveIndex,1);
linechart.series = tmp;
linechart.invalidateSeriesStyles();
}


The default series gets added with the CreationComplete method:

private function init():void
{
addToChart('Amount');
Amount.selected = true;
}


Anyway, I hope that wasn't too confusing. I should really start hosting these little examples somewhere with the ViewSorce available. If you need more clarification just gimmie a comment here.

Publishing source code in Blogger

Wow it worked! Sucks I still have to escape all my > and < s but way better than what I was doing before (manual formatting).

So this took me a while to figure out... maybe I can do a step-by-step to help someone else out there out... there are a lot of pages on how to do this, I needed to combine them to make it work.

K... so... inside your blogger Dashboard go Layout - Edit HTML.

Paste this just before the </head> tag:


<link href='http://syntaxhighlighter.googlecode.com/svn/trunk/Styles/SyntaxHighlighter.css' rel='stylesheet' type='text/css'/>


Then paste this just before the </body> tag


<script language='javascript' src='http://syntaxhighlighter.googlecode.com/svn/trunk/Scripts/shCore.js'/>

<script language='javascript' src='http://syntaxhighlighter.googlecode.com/svn/trunk/Scripts/shBrushCSharp.js'/>

<script language='javascript' src='http://syntaxhighlighter.googlecode.com/svn/trunk/Scripts/shBrushXml.js'/>

<script language='javascript' src='http://syntaxhighlighter.googlecode.com/svn/trunk/Scripts/shBrushPython.js'/>

<script class='javascript'>
//<![CDATA[
function FindTagsByName(container, name, Tag)
{
var elements = document.getElementsByTagName(Tag);
for (var i = 0; i < elements.length; i++)
{
if (elements[i].getAttribute("name") == name)
{
container.push(elements[i]);
}
}
}

var elements = [];
FindTagsByName(elements, "code", "pre");
FindTagsByName(elements, "code", "textarea");

for(var i=0; i < elements.length; i++) {
if(elements[i].nodeName.toUpperCase() == "TEXTAREA") {
var childNode = elements[i].childNodes[0];
var newNode = document.createTextNode(childNode.nodeValue.replace(/<br\s*\/?>/gi,'\n'));
elements[i].replaceChild(newNode, childNode);
}
else if(elements[i].nodeName.toUpperCase() == "PRE") {
brs = elements[i].getElementsByTagName("br");
for(var j = 0, brLength = brs.length; j < brLength; j++) {
var newNode = document.createTextNode("\n");
elements[i].replaceChild(newNode, brs[0]);
}
}
}

//clipboard does not work well, no line breaks
//dp.SyntaxHighlighter.ClipboardSwf = "http://syntaxhighlighter.googlecode.com/svn/trunk/Scripts/clipboard.swf";
dp.SyntaxHighlighter.HighlightAll("code");
//]]>
</script>


Save your template.

Then to test it out create a new post and put some sample code between two PRE tags, like this:

<pre name="code" class="html">
<!-- code here -->
</pre>

BUT you have to use escape characters for html
Here's a link to a site to help you do that quickly:
http://www.accessify.com/tools-and-wizards/developer-tools/quick-escape/default.php


And here are the links to the posts that helped me figure this out:
http://www.accessify.com/tools-and-wizards/developer-tools/quick-escape/
http://azowebsphere.blogspot.com/2008/08/how-to-post-code-snippets-in-blogger.html

Publish Source code in Blogger??

testing testing one two three...



<html>
</html>

Friday, December 5, 2008

Thursday, November 27, 2008

Flex embed an image as a Class

// Embed the image once and refer to the Class
[Bindable]
[Embed(source="../assets/images/myImageFile.png")]
public var myImage:Class;


This is helpful if you're using the same image in several places, this way you only have to embed it once, and if you change the image file name you only have to change it in one place.

You can also create a Class that holds all the images and you can get an instance of that Class to refer to your images.

For example:

package com.view
{
 import mx.binding.utils.BindingUtils;

 [Bindable]
 public class AssetImporter
 {

  private static var instance : AssetImporter;

  //return singleton instance of AssetImporter

  public static function getInstance() : AssetImporter
  {
   if (instance == null)
   instance = new AssetImporter();
   return instance;
  }

  //put your images here
  //One
  [Embed(source="/assets/images/myImages.swf#one")]
  public var imgOne:Class;

  //Two
  [Embed(source="/assets/images/two.png")]
  public var twoLarge:Class;
 }
}


Then in your mxml script use the following lines

import assets.AssetImporter;
[Bindable] private var assetImporter:AssetImporter = AssetImporter.getInstance();


then to call an image simply refer to the instance

<mx:Image source="{assetImporter.imgOne}"/>


Here is an example of an AssetImporter with view source enabled.

Tuesday, November 25, 2008

Flex 3 AdvancedDataGrid dataTipFunction mess

Well Flex 3 with it's fancy AdvancedDataGrids is proving to be a little more than frustrating. All I wanted to do was show a dataTip on a hover on the datagrid row. It took me hours of research to figure out that there is a "bug":

http://bugs.adobe.com/jira/browse/FLEXDMV-1789

So if you have an Advanced Data Grid and you want to show a custom data tip on hover of a row this is basically how you should do it.


private function testTip(item:Object):String
{
 if (item is AdvancedDataGridColumn)
  return "This is a header tip"
 else
  return "This is a data tip";
}

OH yes... that's right. It calls this beauty twice. If you don't have the if statement it will error out and your ap won't run. Sweet eh?


<mx:AdvancedDataGrid
 id="mainADG"
 width="100%" height="100%"
 dataTipFunction="testTip">
[...]
 <mx:AdvancedDataGridColumn
  dataField="Name"
  headerText="Name"
  itemRenderer="com.view.renderers.myRenderer"
  showDataTips="true"
 />
[...]


NOW.. if you're like me, you might be using an itemRenderer on that column. You try out the datatip stuff above and curse me because it doesn't work. Yeah. I know.

Inside your item renderer you're going to have to set the tooltip (that is if what you're extending has a tooltip). I usually extend label, so it works fine.


package com.view.renderers
{
 import mx.controls.Label;

 public class MyRenderer extends Label
 {
  override protected function updateDisplayList(unscaledWidth:Number,   unscaledHeight:Number):void
  {
   super.updateDisplayList(unscaledWidth, unscaledHeight);

   //Set the font color based on the item number.
   setStyle("color", (data.myNumber<= 0) ? 'red' : 'green');
   this.toolTip = "Here is my custom data/tool tip!";
  }
 }
}

Monday, November 17, 2008

embed an image

...also of note...

a Flex embed statement in css looks like this
upSkin: Embed("images/buttonUp.png");

a Flex embed statement inline looks like this
icon="@Embed('images/add.png')"

a chart gutter

Note to self... you've seen this before, stop forgetting it...

The area around a Flex chart where the axis lives is called the "gutter". There are 4 properties that control this:

  • gutterLeft

  • gutterRight

  • gutterTop

  • gutterBottom


https://www.adobe.com/livedocs/flex/201/html/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Book_Parts&file=charts_formatting_110_11.html

Here is an example using chart gutters.

Tuesday, October 21, 2008

Today I'm a developer

For the last few weeks I have been a Flex developer, and I've learned about something really helpful.

Flex value objects.

Well these little AS classes have saved me some heartache and headaches. I was dumping everything into an xmlListCollection and then creating ArrayCollections of display objects with the data from the xmlListCollection. It started to get messy. No more! Value objects are way easier to manage.

Here’s an example of how to create one in case you’ve never done it before. (ps this is using the Cairngorm framework).

Okay first things first. What are you going to pull from your xml? In my example I am pulling a "Section" and each Section has a "SubSection". In this case I'd need two value objects. One for my Section and one for my SubSection. Stick them in a folder called vo.

Here is my SectionVO.as:


package com.vo
{
import com.adobe.cairngorm.vo.ValueObject;
import mx.collections.ArrayCollection;

[Bindable]
public class SectionVO implements ValueObject
{
public var title:String = "NA";
public var subsections:ArrayCollection = new ArrayCollection();

}
}


Each Section has a title and a bunch of subsections. So I have a String and an ArrayCollection.

Here is my SubSectionVO.as:

package com.vo
{
import com.adobe.cairngorm.vo.ValueObject;

[Bindable]
public class SubSectionVO implements ValueObject
{
public var title:String = "NA";
public var header:String = "NA";
public var body:String = "NA";

}
}


Each SubSection has a title, a header, and a body. So I have 3 public String vars.

Inside your main Application mxml file call the HTTP service and use the data to populate the value objects.

Here is the result handler. It gets your XML and puts it inside an XMLList. Then it calls the method that populates the value objects. Once that’s done it adds the initial “View” object (i.e. everything you see on the screen when the Application loads).


// Result handler - gets called after RSS is loaded.
private function contentResultHandler(event:ResultEvent):void
{
// Get sections from XML
var xmlList:XMLList = XML(event.result).sections.section;
fillSections( xmlList );

// Add child to show view now that all content is loaded
var view:View = new View();
view.id="view";
this.addChild(view)
}


This method populates the value objects and then adds each Section to an ArrayCollection inside the ModelLocator that will be used throughout the rest of the application.


// Populate Section VOs with data and add to ML.allsectionsac
public function fillSections( xmlList:XMLList ):void
{
// For each section in the data
for each ( var sectionData:XML in xmlList )
{

// Populate the Section data
var sectionVO:SectionVO = new SectionVO();
sectionVO.title = sectionData.title;

// For each sub-section in the section
for each ( var subSectionData:XML in sectionData.subsections.subsection )
{
// Populate the SubSection
var subsectionVO:SubSectionVO = new SubSectionVO();
subsectionVO.title = subSectionData.title;
subsectionVO.header = subSectionData.header;
subsectionVO.body = subSectionData.body;

// Add SubSectionVO to the SectionVO
sectionVO.subsections.addItem(subsectionVO);
}

// Add section VOs to ModelLocator array collection
ML.allSectionsac.addItem(sectionVO);
}
}


And of course, you're going to need your fault handler in case the Application can't find your xml data in the first place.


// Fault handler - displays the error.
private function contentFaultHandler(event:FaultEvent):void
{
Alert.show(event.fault.message, "Could not load content feed");
}


Inside your MXML create the HTTPService object.


<HTTPService
id="contentService"
url="assets/xml/content.xml"
resultformat="e4x"
result="contentResultHandler(event)"
fault="contentFaultHandler(event)">
</mx:httpservice>


And that’s it! Now all your data from the xml is easily accessible inside your application.

Now all you do is use your ArrayCollection inside your ModelLocator. You will probably have to create a Display Object component as well. Your Display Object would have one Bindable public var of type your value object (i.e. [Bindable] public var sectionObj:SectionVO; ). In my example below I also refer to a SubSection object, which just be something simlar to this section object but with text objects getting their data from the SubSectionVO.

The Section Display Object component may look something like this:

Section.mxml

<mx:VBox
mx="http://www.adobe.com/2006/mxml"
com="com.*">

<mx:script>
<![CDATA[
import com.vo.SubSectionVO;
import com.vo.SectionVO;

import model.ModelLocator;
[Bindable] private var ML:ModelLocator = ModelLocator.getInstance();

[Bindable] public var sectionObj:SectionVO;

public function init():void
{
getSubSections();
}

// Get all sub-sections and add them to section
public function getSubSections():void
{
for ( var n:int=0; n < sectionObj.subsections.length; n++ )
{
var subSectionView:SubSection = new SubSection();
subSectionView.subSectionObj = SubSectionVO(sectionObj.subsections.getItemAt(n));
this.addChild(subSectionView);
}
}
]]>
</mx:Script>

<mx:Label text="{sectionObj.title}" />

</mx:VBox>


Hope that was SOMEWHAT clear. Hope that helps someone out someday. I am sure I’ll be looking back on this post when I forget how to do this!

Tuesday, September 2, 2008

iPod touch

My first post from my new iPod touch. Yeah, I'm totally just showing off.

Tuesday, August 26, 2008

Using html links in flex with xml data

So I'm not sure why this took me so long to figure out since it's actually quite simple...

I have a flex application that is pulling all its data from an xml file. I wanted to add links to certain pieces of text. I finally figured out a way to make it work... if this is the best way or not, I do not know, but it works! (This is why I'm not a programmer... haha).

Use a combination of xml file with node wrapped in the ![CDATA[ tag, Text component, and the htmlText property.

Example:

MXML snip:


<mx:HTTPService
id="contentService"
url="assets/xml/content.xml"
resultFormat="e4x"
result="contentResultHandler(event)"
fault="contentFaultHandler(event)"/ >


ActionScript:
iText = new Text();
iText.htmlText = xmlListColl.getItemAt(n).writeup;


XML snip:

<section>
<title>my writeup </title >
<writeup>
<![CDATA[Here is a <font style="color:'#0000FF';"> <a href="'http://www.mylinkaddress.com'" target="'_blank'">LINK </a > </font > that shows up blue and is clickable and opens the link in a new browser window.]] >
</writeup >
</section >


YAY :)

Thursday, August 14, 2008

the almighty Google looses a round?

Over the past month or so I have been working with an IA and some developers to create a proof of concept desktop widget. After the IA did some research and we decided on some wireframes, I did the visual and interaction design, sent the developers my mock ups and assets and let them go. The point of the exercise was to mainly get a sales tool, but also to discover the limitations between platforms.

The three that have been created so far have been a Google Gadget, a Yahoo Widget, and an Adobe Air application. According to the developer working on the Google Gadget it is a nightmare. It came out looking the least like my mock ups that's for sure. The Yahoo one looks pretty nice, but it doesn't do any of the cool transitions and stuff I asked for. The Air, well, I guess I'm an Adobe lover anyway, but it came out the best by far. I think that maybe what we were trying to create may have been a bit beyond Google and even Yahoo’s capabilities.

I wish I could show you, but yeah... company policy and all that.

And I know, I know. I need to design my own damn background for this blog. I’ll get around to it eventually... maybe...

Thursday, July 17, 2008

cool iPhone buttons

The last few days I have been working on designing a widget for my company along with an information acritect. It's going pretty well. My inital design looked like an iPhone, and then we moded it form there until we came up with a simple yet cool design (something like 10 mock ups later, lol). My buttons look like iPhone buttons, which I love (I wish I actually had an iPhone, but they're too expensive to own in Canada). On the over state I increased the brightness by 30, suble but nice, and on the down they look depressed.

Here's a sample for fun. :)
iPhone Button

The awesome tutorial to do the buttons I found here - http://blog.jotlet.net/2007/07/05/iphone-style-icon-tutorial/ .

To do the over and down effects I came up with all you need to do is the following:


The "down style" layer is just a copy of the color layer with an inner shadow:

I also nudged the icon down 1px and right 1px.

The "over style" is just an adjustment brightness/contrast layer with the brightness +30.

Thursday, July 10, 2008

The "IT" pigeon hole

Does it count that I don’t want to be put in the "IT" box? That I think design is about everything from a cell phone interface to the way your local grocery store is laid out? Does it matter that some day I want to use my skills to change the devices I use daily that drive me nuts – like my oven?

"Some of what good interaction designers do is make the world better by removing those little irritants in life, some of which we don't know exist until they are gone." - Dan Saffer from his book Designing for Interaction

Wednesday, July 9, 2008

What's my title today?

This blog is named such because depending on the day I have a different job to do.

Yesterday I was a visual designer.

Today I am a technical writer.

Next week I expect I will be an interaction designer.


When it all comes down to it, I may be a generalist, but if there were such thing as a generalist that focused on the end user I suppose that would be me.

Problem solver and user empathizer extraordinaire.

With an educational background in web design and development, and years of experience coding, I decided to leave the code behind (without any tears) and pursue what I actually cared about; the experience. UI professionals may all agree that we are underappreciated but necessary and often overlooked until the last second of any project. I want to help change that, in my own company, and in the industry as a whole.

Just yet another IT professional blog to crowd the already overcrowded web space.

But hey, who’s listening?