Flex IBM ILOG (Elixir 3.0) Interactive map - apache-flex

I develop interactive map: user can zoom some area and place labels by mouse click.
I am creating customSkin:
<ibm:Scroller top="0" left="0" width="100%" height="100%" id="scroller" doubleClickEnabled="true"
skinClass="com.ibm.ilog.elixir.skins.spark.ScrollerSkin">
<ibm:MapGroup id="mapGroup">
<ibm:MapFeatureGroup id="featureGroup" featureRenderer="skins.DistrictRenderer" keyField="district"/>
<ibm:MapDataGroup id="objectsDataGroup"
zoomable="false"
itemRenderer="skins.DistrictObjectsRenderer"
keyField="district" />
</ibm:MapGroup>
</ibm:Scroller>
In DistrictRenderer I dispatching mouse click.
But how can I place labels?
May be I must pass x,y to DistrictObjectsRenderer and there create new labels?

Its a final solution:
RegionMapSkin.mxml:
<ibm:Scroller top="0" left="0" width="100%" height="100%" id="scroller" skinClass="com.ibm.ilog.elixir.skins.spark.ScrollerSkin">
<ibm:MapGroup id="mapGroup">
<ibm:MapFeatureGroup id="featureGroup"
featureRenderer="com.skins.DistrictRenderer" />
<ibm:MapDataGroup id="objectsGroup"
zoomable="false"
itemRenderer="com.skins.ObjectsRenderer"
dataProvider="{ObjectsModel.getInstance().items}" />
</ibm:MapGroup>
</ibm:Scroller>
ObjectsRenderer.mxml:
<?xml version="1.0" encoding="utf-8"?>
<s:DataRenderer xmlns="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:ibm="http://www.ibm.com/xmlns/prod/ilog/elixir/2010"
left="{data.lon}"
top="{data.lat}">
<s:Label text="{data.title}"
color="#ffffff"
backgroundColor="#AB191A"
paddingLeft="3" paddingRight="3" paddingTop="4" paddingBottom="1"
top="-18" left="3"/>
<s:Ellipse width="6" height="6">
<s:fill>
<s:SolidColor color="#AB191A" />
</s:fill>
</s:Ellipse>
</s:DataRenderer>
And onMouseClick handler in DistrictRenderer:
private function onMouseClick(e:MouseEvent):void
{
var mapFeature:MapFeature = MapFeatureRenderer(e.currentTarget).mapFeature;
var map:RegionMap = app.map;
var p:Point = new Point(e.localX, e.localY);
p = map.mapToLongLat(p);
ObjectsModel.getInstance().items.addItem({
'title': parameters.title,
'lon': parameters.lon,
'lat': parameters.lat
});
}

Related

How to control the display of legend in Pie Chart as per our need?

In my Flex application, I have a pie chart which shows the distribution of shares held by different people in a company.
[Bindable]
private var shareData:ArrayCollection;
<mx:PieChart id="sharesDistribution" dataProvider="{shareData}" showDataTips="true"
height="100%" width="70%" left="0" right="0" bottom="0" top="0" >
<mx:series>
<mx:PieSeries field="share" nameField="name" labelPosition="insideWithCallout" />
</mx:series>
</mx:PieChart>
<mx:Legend right="0" width="30%" direction="horizontal" dataProvider="{sharesDistribution}" />
As per the above code, the Pie Chart appears properly. The problem I'm facing here is....
Sometime, any of the member may have 0 or 0% shares. During that instance, the pie chart displays only the people who have shares. Say for e.g A, B, C & D are holding shares in a company. At one point of time, C doesn't hold any share, the pie chart shows only the shares held by A,B & D.
But, the legend shows the person C also with a colour which is not actually looking correct since the pie chart doesn't show such a person. Hope, you understand my point and agree with me.
I want the person C not to be displayed in the legend.
How can I do that ? Kindly, Share your answers.
I think the pie chart component do always show all items from the data source.
I would process the change Event of the ArrayList and define another List, which does not content any null elements.
Something like this (here is a working example):
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="955" minHeight="600" creationComplete="init(event)">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.events.CollectionEvent;
import mx.events.FlexEvent;
[Bindable]private var shareData:ArrayCollection = new ArrayCollection(
[
{name: "A", share: 20},
{name: "B", share: 50},
{name: "C", share: 0},
{name: "D", share: 10}
]);
[Bindable]private var shareDataDP:ArrayCollection = new ArrayCollection();
protected function init(event:FlexEvent):void
{
shareData.addEventListener(CollectionEvent.COLLECTION_CHANGE, onCollectionChange);
updateDP();
}
private function onCollectionChange(evt:CollectionEvent):void
{
updateDP();
}
private function updateDP():void
{
shareDataDP.removeAll();
for each (var item:Object in shareData)
{
if (item.share != 0)
{
shareDataDP.addItem(item);
}
}
}
]]>
</fx:Script>
<s:VGroup left="20" top="20">
<s:HGroup verticalAlign="bottom">
<s:Label text="Value for C: "/>
<s:TextInput id="valueC" text="0" width="40" restrict="0-9"/>
<s:Button label="Change" click="{shareData.getItemAt(2).share = int(valueC.text); shareData.refresh()}"/>
</s:HGroup>
<s:HGroup>
<s:Group width="300" height="300">
<mx:PieChart id="sharesDistribution" dataProvider="{shareData}" showDataTips="true"
height="100%" width="70%" left="0" right="0" bottom="0" top="0">
<mx:series>
<mx:PieSeries field="share" nameField="name" labelPosition="insideWithCallout" />
</mx:series>
</mx:PieChart>
<mx:Legend right="0" width="30%" direction="horizontal" dataProvider="{sharesDistribution}" />
</s:Group>
<s:Group width="300" height="300">
<mx:PieChart id="sharesDistributionDP" dataProvider="{shareDataDP}" showDataTips="true"
height="100%" width="70%" left="0" right="0" bottom="0" top="0">
<mx:series>
<mx:PieSeries field="share" nameField="name" labelPosition="insideWithCallout" />
</mx:series>
</mx:PieChart>
<mx:Legend right="0" width="30%" direction="horizontal" dataProvider="{sharesDistributionDP}" />
</s:Group>
</s:HGroup>
</s:VGroup>
</s:Application>

Invisible part of BitmapImage

I have a SkinnableContainer, which contains s:BitmapImage. I need a part of the image (Rect) to be fully transparent. Just make a transparent cut-out, that would be easy to position.
The image in the background is width="300" height="200" and transparent area should be width="200" height="20" and right="0" bottom="50". I managed to achieve similar effect with BlendMode.OVERLAY, but I believe there must be a better way.
Any ideas, suggestions, please?
You could apply another object (like a UIMovieClip) to the image as a Mask. You could also manipulate the pixels of a Bitmap and set it as the source of the BitmapImage. This may or may not make sense depending on your specific requirements.
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
creationComplete="onComplete(event)"
backgroundColor="0xEEEEEE">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
import mx.graphics.ImageSnapshot;
import mx.skins.RectangularBorder;
protected function onComplete(event:Event):void
{
var bW:int = 300;
var bH:int = 200;
var X:int = 0;
var Y:int = 150;
var W:int = 200;
var H:int = 20;
var topRect:Rectangle = new Rectangle(0,0,bW,Y);
var righRect:Rectangle = new Rectangle(W,0,bW-W,bH);
var bottomRect:Rectangle = new Rectangle(0,Y+H,bW,bH-(Y+H));
var targetBitmapData:BitmapData = ImageSnapshot.captureBitmapData(original);
var newBitmapData:BitmapData = new BitmapData(bW,bH,true,0x00000000);
newBitmapData.copyPixels(targetBitmapData, topRect, new Point(0, 0),null, null, true);
newBitmapData.copyPixels(targetBitmapData, righRect, new Point(W, 0),null, null, true);
newBitmapData.copyPixels(targetBitmapData, bottomRect, new Point(0,Y+H),null, null, true);
modified.source = new Bitmap(newBitmapData);
}
]]>
</fx:Script>
<s:VGroup gap="10" left="10" top="10">
<s:Group id="original">
<s:Rect width="300" height="200">
<s:fill>
<s:SolidColor/>
</s:fill>
<s:stroke>
<s:SolidColorStroke color="0xFF0000"/>
</s:stroke>
</s:Rect>
</s:Group>
<s:Group>
<mx:Image width="300" height="200" id="modified" />
<s:Rect top="0" left="0" right="0" bottom="0">
<s:stroke>
<s:SolidColorStroke color="0xFF0000"/>
</s:stroke>
</s:Rect>
</s:Group>
</s:VGroup>
</s:Application>
An example for your particular case, maybe you'll extend this.

Resize layout after a loaded image gets resized

So I'm loading a really big pic in this mx:Image manteinAspectRatio enabled with a height constraint and as usual it doesn't work (Flex is definitely not for me):
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<s:Rect top="0" right="0" bottom="0" left="0">
<s:fill>
<mx:SolidColor color="#000000"/>
</s:fill>
</s:Rect>
<s:VGroup id="a" top="0" right="0" bottom="0" left="0" horizontalAlign="center" verticalAlign="middle">
<s:Group id="b">
<s:Rect id="c" top="0" right="0" bottom="0" left="0" radiusX="10" radiusY="10">
<s:fill>
<mx:SolidColor color="#cccccc"/>
</s:fill>
</s:Rect>
<s:VGroup id="d" paddingTop="10" paddingRight="10" paddingBottom="10" paddingLeft="10">
<mx:Image id="e" source="big.jpg" maxHeight="300" maintainAspectRatio="true" />
</s:VGroup>
</s:Group>
</s:VGroup>
</s:Application>
The big.jpg has more width than height so when it's resized the wrapping containers get the correct new height but they still have the old width.
I've tried to call validateNow() on every element, invalidate, callLater(), resize event, compete event... I'm a noob getting tired of AS 3.0
UPDATE:
Reading J_A_X answer I ended up with this:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<fx:Script>
<![CDATA[
import flash.events.Event;
import mx.controls.Image;
private function fix(event:Event):void {
var img:Image = event.target as Image;
var h:uint = 300;
img.width *= h / img.height;
img.height = h;
img.removeEventListener("updateComplete", fix);
}
]]>
</fx:Script>
<s:Rect top="0" right="0" bottom="0" left="0">
<s:fill>
<mx:SolidColor color="#000000"/>
</s:fill>
</s:Rect>
<s:VGroup top="0" right="0" bottom="0" left="0" horizontalAlign="center" verticalAlign="middle">
<s:Group>
<s:Rect top="0" right="0" bottom="0" left="0" radiusX="10" radiusY="10">
<s:fill>
<mx:SolidColor color="#cccccc"/>
</s:fill>
</s:Rect>
<s:VGroup paddingTop="10" paddingRight="10" paddingBottom="10" paddingLeft="10">
<mx:Image source="big.jpg" maintainAspectRatio="false" updateComplete="fix(event)" />
</s:VGroup>
</s:Group>
</s:VGroup>
</s:Application>
I hope that helps another one dealing with Flex.
After looking at Image, it seems that it needs explicit height/width or else it won't scale down properly. It's a bit stupid not to scale to the maxHeight, but you could always use Flex 4.5 Image class instead or just create your own to handle max height/width.

Flex: Custom ProgressBar bar skin won't fill entire track

I'm trying to create a simple skin for the flex progressbar control. Both the track and the bar should have rounded corners, and the bar should fill the track completely in the parts where it is being shown.
Here is the bar skin I've created based off this example:
<?xml version="1.0" encoding="utf-8"?>
<s:SparkSkin
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<fx:Script>
<![CDATA[
override protected function initializationComplete():void {
useChromeColor = true;
super.initializationComplete();
}
]]>
</fx:Script>
<s:Rect radiusX="5" radiusY="5" top="0" left="0" right="0" bottom="0">
<s:fill>
<s:SolidColor color="#bb0203" />
</s:fill>
</s:Rect>
</s:SparkSkin>
Here is the progress bar declaration:
<mx:ProgressBar id="progressBar" name="progressBar" top="40" left="10" width="480" height="25"
label="" labelWidth="0"
trackSkin="Skins.ProgressBar.TrackSkin"
barSkin="Skins.ProgressBar.BarSkin" />
And here is the track skin:
<?xml version="1.0" encoding="utf-8"?>
<s:SparkSkin
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minHeight="25">
<fx:Script>
<![CDATA[
override protected function initializationComplete():void {
useChromeColor = true;
super.initializationComplete();
}
]]>
</fx:Script>
<s:Rect width="100%" height="100%" radiusX="5" radiusY="5">
<s:fill>
<s:SolidColor color="#d1d3d4" />
</s:fill>
</s:Rect>
</s:SparkSkin>
Unfortunately, it doesn't have quite the desired effect:
Instead of the bar being flush with the track, there's a margin, and the rounded border gets cut off.
How do I fix this?
In addition to setting the barSkin property, set the maskSkin property to the same value as barSkin:
You can assign this skin the same way as others in your code:
<mx:ProgressBar id="progressBar" name="progressBar" top="40" left="10" width="480" height="25"
label="" labelWidth="0"
trackSkin="Skins.ProgressBar.TrackSkin"
maskSkin="Skins.ProgressBar.BarSkin"
barSkin="Skins.ProgressBar.BarSkin" />

Difficulty creating animated skin in Flex 4

I have been tasked with creating the equivalent Flex 4 implementation of graphical style of the buttons found on this page:
http://h.dwighthouse.com/temp/UDOP/2011-3-25_themeDevGlowing/
Namely, the animated glowing effect on the buttons. I've gotten what I think is the appropriate programmatic gradient-based skin basis, but I have two problems.
One, I can't get any mouse events to respond in the skin. I can't seem to find others with this problem. In the below code, startAnimation is never getting called when a mouse over occurs, but the event is apparently getting registered. To test this code, simply add skinClass="ButtonSkin" to a button declaration in your main application, the code below being the ButtonSkin.mxml file.
<?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"
xmlns:mx="library://ns.adobe.com/flex/mx"
initialize="init()"
mouseEnabled="true">
<fx:Metadata>
[HostComponent("spark.components.Button")]
</fx:Metadata>
<fx:Script>
<![CDATA[
import flash.events.Event;
[Bindable]
private var animatedAlpha:Number = 0.8;
private var animationDirection:Number = -1;
private function backAndForth(value:Number, max:Number, min:Number, increment:Number):Number {
value += (animationDirection * increment);
if (value < min || value > max)
{
animationDirection *= -1;
value += (animationDirection * increment * 2);
}
return value;
}
private function startAnimation(e:MouseEvent):void {
animatedAlpha = backAndForth(animatedAlpha, 0.8, 0.3, 0.1); // Or something
systemManager.addEventListener(MouseEvent.MOUSE_OUT, endAnimation);
}
private function endAnimation(e:MouseEvent):void {
animatedAlpha = backAndForth(animatedAlpha, 0.8, 0.3, 0.1); // Or something
systemManager.removeEventListener(MouseEvent.MOUSE_OUT, endAnimation);
}
private function init():void {
parent.mouseChildren = true; // Otherwise mouse events don't fire in the skin
clickableArea.addEventListener(MouseEvent.MOUSE_OVER, startAnimation);
}
]]>
</fx:Script>
<s:states>
<s:State name="up" />
<s:State name="over" />
<s:State name="down" />
<s:State name="disabled" />
</s:states>
<s:Group top="0" bottom="0" left="0" right="0" id="clickableArea">
<s:Rect top="0" bottom="0" left="0" right="0" radiusX="8" radiusY="8">
<s:fill>
<s:LinearGradient rotation="90">
<s:GradientEntry color="#000000" color.down="#bb0000" color.disabled="#3b3b3b" ratio="0" />
<s:GradientEntry color="#353535" color.down="#ff0000" color.disabled="#555555" ratio="1" />
</s:LinearGradient>
</s:fill>
</s:Rect>
<s:Rect top="0" bottom="0" left="0" right="0" radiusX="8" radiusY="8">
<s:fill>
<s:LinearGradient rotation="90">
<s:GradientEntry color.over="#8f1b1b" alpha="0" ratio="0" />
<s:GradientEntry color.over="#8f1b1b" alpha="0" ratio="0.4" />
<s:GradientEntry color.over="#8f1b1b" alpha="{animatedAlpha}" alpha.down="0" ratio="1" />
</s:LinearGradient>
</s:fill>
</s:Rect>
<s:Rect top="0" bottom="0" left="0" right="0" radiusX="8" radiusY="8">
<s:stroke>
<s:SolidColorStroke color="#000000" color.disabled="#333333" weight="1" />
</s:stroke>
<s:fill>
<s:LinearGradient rotation="90">
<s:GradientEntry color="#ffffff" color.disabled="#9e9e9e" alpha="0.6" alpha.down="0.7" ratio="0" />
<s:GradientEntry color="#ffffff" color.disabled="#848484" alpha="0.2" alpha.down="0.4" ratio="0.45" />
<s:GradientEntry color="#ffffff" alpha="0" ratio="0.46" />
</s:LinearGradient>
</s:fill>
</s:Rect>
<s:Label id="labelDisplay" color="#ffffff" color.disabled="#aaaaaa" textAlign="center" baseline="center" paddingTop="7" paddingBottom="5" paddingLeft="10" paddingRight="10">
<s:filters>
<s:DropShadowFilter distance="0" angle="45" blurX="5" blurY="5" />
</s:filters>
</s:Label>
</s:Group>
EDIT I found out why mouse events weren't firing. The button apparently has mouseChildren false by default, which specifically prevents events from firing lower than itself. Using parent.mouseChildren = true; in the constructor fixes this issue. On to the next part of the question: continuous animation.
Other than that, I'd appreciate some help or point me in the right direction of calling a function every frame, and the ability to turn said incremental call on and off using mouse events. (If that's not how you animate in Flex, forgive me, I know nothing about Flash-based animation.) Thanks!
Answers to both questions have been found:
Buttons prevent their children (including Skins) from receiving mouse events. By setting parent.mouseChildren = true; in the skin's initializing function, mouse events are received normally.
Turns out you can animate in Flex (Actionscript) the same way you animate in Javascript: setInterval and clearInterval.

Resources