How to add a Image to the ToolTip? - apache-flex

I want to add a image to the tooTip that i have have currently. So that when u rollover the row its show a the ship information and a image of a ship. I want the image to be displayed according to the type of ship. There is mainly 3 type. So according to the type i will display the image and the text information about the ship. For now I just used dummy data.I dont know if the toolTip can support to hold an image.
So my Ouestions:
How to add an image to the existing tool tip i have(in the yellow box)
how to add the image in away which is not really static so that I can display according to ship type later on.
Pls can someone help me with this.
Thank you soo much :)
This is the code I currently have (Its the entire codes so u can run on ur pc if u want) :
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.controls.Alert;
import spark.events.GridEvent;
import spark.components.Image;
[Bindable]
private var myArrivalShips:ArrayCollection = new ArrayCollection([
{arrivalShipsName:"Ship A", ETD:"12 March"},
{arrivalShipsName:"Ship B", ETD:"25 March"}
]);
private function buildToolTip(item:Object,column:GridColumn):String{
var myString:String = "";
if(item != null)
{
myString = myString +"Ship name : " + item.arrivalShipsName + "\n";
myString = myString + "ETD : " + item.ETD + "\n" +"Service: Repair"+"\n"+"Length of ship : 50"+"\n"+"Agent contact: 982392398";
}
return myString;
}
]]>
</fx:Script>
<s:BorderContainer x="267" y="11" width="331" height="586">
<s:DataGrid id="arrivalTable" x="10" y="326" width="302" height="205" requestedRowCount="4" dataProvider="{myArrivalShips}" showDataTips="true" dataTipFunction="buildToolTip">
<s:columns>
<s:ArrayList>
<s:GridColumn dataField="arrivalShipsName" headerText="Arrival Ships"></s:GridColumn>
<s:GridColumn dataField="ETD" headerText="ETD"></s:GridColumn>
</s:ArrayList>
</s:columns>
</s:DataGrid>
<s:BorderContainer x="10" y="19" width="302" height="285">
</s:BorderContainer>
</s:BorderContainer>

You have 2 case:
Replace ToolTipManagerImpl with own modified ToolTipManagerImpl, where you switch toolTipClass with own custom layout and skin. You can find examples.
Create custom tooltip, extends UIComponent implements IToolTip. Prototype look like original tooltip:
Main application mxml:
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
creationComplete="creationCompleteHandler(event)"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.events.FlexEvent;
import mx.events.ToolTipEvent;
import mx.managers.ToolTipManager;
[Bindable]
private var myArrivalShips:ArrayCollection = new ArrayCollection([
{arrivalShipsName:"Ship A", ETD:"12 March", image: "http://url"},
{arrivalShipsName:"Ship B", ETD:"25 March", image: "http://url"}
]);
private function buildToolTip(item:Object,column:GridColumn):String
{
return JSON.stringify(item);
}
protected function creationCompleteHandler(event:FlexEvent):void
{
ToolTipManager.toolTipClass = ToolTipCustomClass;
}
]]>
</fx:Script>
<fx:Declarations></fx:Declarations>
<s:BorderContainer x="267" y="11" width="331" height="586">
<s:DataGrid id="arrivalTable" x="10" y="326" width="302" height="205" requestedRowCount="4" dataProvider="{myArrivalShips}"
showDataTips="true" dataTipFunction="buildToolTip">
<s:columns>
<s:ArrayList>
<s:GridColumn dataField="arrivalShipsName" headerText="Arrival Ships"></s:GridColumn>
<s:GridColumn dataField="ETD" headerText="ETD"></s:GridColumn>
</s:ArrayList>
</s:columns>
</s:DataGrid>
<s:BorderContainer x="10" y="19" width="302" height="285">
</s:BorderContainer>
</s:BorderContainer></s:Application>
and class ToolTipCustomClass:
package
{
import flash.display.DisplayObject;
import flash.events.Event;
import mx.controls.Image;
import mx.controls.ToolTip;
import mx.core.EdgeMetrics;
import mx.core.IRectangularBorder;
import mx.core.mx_internal;
import mx.styles.ISimpleStyleClient;
import mx.utils.ObjectUtil;
use namespace mx_internal;
public class ToolTipCustomClass extends ToolTip
{
private var _image:Image;
public function ToolTipCustomClass()
{
super();
}
override protected function createChildren():void
{
mx_internal::createTextField(-1);
createBorder();
textField.visible = false;
_image = new Image();
_image.mouseChildren = false;
_image.mouseEnabled = false;
_image.addEventListener(Event.COMPLETE, onLoadCompliteHandler);
addChild(_image);
}
protected function onLoadCompliteHandler(event:Event):void
{
_image.removeEventListener(Event.COMPLETE, onLoadCompliteHandler);
textField.x = _image.contentWidth;
textField.width = textField.width - _image.width;
invalidateDisplayList();
}
override protected function measure():void
{
super.measure();
_image.width = 100;
_image.height = 100;
}
override public function set text(value:String):void
{
if (!value) return;
var str:String = "";
var item:Object;
try
{
item = JSON.parse(value);
} catch (e:Error) {}
if (item != null)
{
str += "Ship name : " + item.arrivalShipsName + "\n" +
"ETD : " + item.ETD + "\n" +
"Service: Repair\n" +
"Length of ship : 50\n" +
"Agent contact: 982392398";
_image.source = item.image;
}
super.text = str;
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
if (_image && _image.content)
{
var bm:EdgeMetrics = borderMetrics;
var leftInset:Number = bm.left + getStyle("paddingLeft");
var topInset:Number = bm.top + getStyle("paddingTop");
var rightInset:Number = bm.right + getStyle("paddingRight");
var bottomInset:Number = bm.bottom + getStyle("paddingBottom");
var widthSlop:Number = leftInset + rightInset;
var heightSlop:Number = topInset + bottomInset;
_image.move( leftInset, topInset );
border.setActualSize(unscaledWidth + _image.contentWidth, Math.max(unscaledHeight, _image.contentHeight));
textField.move(_image.contentWidth, topInset);
textField.setActualSize(unscaledWidth - widthSlop - _image.contentWidth, unscaledHeight - heightSlop);
textField.visible = true;
}
}
private function createBorder():void
{
if (!border)
{
var borderClass:Class = getStyle("borderSkin");
if (borderClass != null)
{
border = new borderClass();
if (border is ISimpleStyleClient)
ISimpleStyleClient(border).styleName = this;
addChildAt(DisplayObject(border), 0);
invalidateDisplayList();
}
}
}
private function get borderMetrics():EdgeMetrics
{
if (border is IRectangularBorder)
return IRectangularBorder(border).borderMetrics;
return EdgeMetrics.EMPTY;
}
}
}
Don't forget replace image url in items object.

Related

Combobox NoClose when embedded Checkbox is clicked [FLEX]

I've reached the point where I have got a dropdown in which is embedded a tree. Each node has got a checkbox.
The idea is to be able to navigate and tick the checkboxes without having the dropdown closing.
Can't make this dropdown to remain open after a checkbox has been clicked!
The event that closes the dropdown occurs when the xml is updated in PermissionTreeItemRendererV2.as > handleChkClick(evt) > this.itemXml.#checked = {"0" or "1"};
Any idea how to tweak the code to disable this annoying event?
sample_combobox.mxml:
<?xml version="1.0"?>
<mx:Application xmlns:local="local.*"
xmlns:mx="http://www.adobe.com/2006/mxml"
creationComplete="loadXML()">
<mx:Script>
<![CDATA[
import mx.collections.XMLListCollection;
import mx.rpc.events.ResultEvent;
import mx.rpc.http.mxml.HTTPService;
public var xmlService:HTTPService = new HTTPService();
[Bindable]
public var xmlResult:XML;
[Bindable]
public var xmlList:XMLList;
[Bindable]
public var xmlTeams:XMLListCollection;
public function loadXML():void
{
xmlService.url = "mlb.xml"
xmlService.resultFormat = "e4x";
xmlService.addEventListener(ResultEvent.RESULT, resultHandler);
xmlService.send();
}
public function resultHandler(event:ResultEvent):void
{
xmlResult = XML(event.result);
xmlList = xmlResult.league;
xmlTeams = new XMLListCollection(xmlList);
}
]]>
</mx:Script>
<local:TreeComboBox
width="300"
id="combo"
labelField="#label" dataProvider="{xmlTeams}" />
</mx:Application>
TreeCombobox.mxml:
<?xml version="1.0" encoding="utf-8"?>
<local:ComboBoxNoClose xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:local="local.*">
<mx:Script>
<![CDATA[
import mx.events.FlexEvent;
[Bindable]
private var _label:String;
[Bindable]
public var treeSelectedItem:Object;
public function updateLabel(event:*):void
{
_label = event.currentTarget.selectedItem[this.labelField];
treeSelectedItem = event.currentTarget.selectedItem;
}
override protected function updateDisplayList(unscaledWidth:Number,
unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
if(dropdown && _label != null){
text = "";//_label;
}
}
]]>
</mx:Script>
<local:dropdownFactory>
<mx:Component>
<mx:Tree change="outerDocument.updateLabel(event)" height="500"
width="500"
itemRenderer="local.PermissionsTreeItemRendererV2"
folderClosedIcon="{null}"
folderOpenIcon="{null}"
defaultLeafIcon="{null}" />
</mx:Component>
</local:dropdownFactory>
</local:ComboBoxNoClose>
PermissionTreeItemRendererV2.as
// ActionScript file
package local
{
import flash.events.Event;
import flash.events.MouseEvent;
import mx.collections.ArrayCollection;
import mx.collections.ArrayList;
import mx.collections.ListCollectionView;
import mx.controls.CheckBox;
import mx.controls.treeClasses.TreeItemRenderer;
import mx.controls.treeClasses.TreeListData;
public class PermissionsTreeItemRendererV2 extends TreeItemRenderer{
public var chk:CheckBox;
public var itemXml:XML;
public function PermissionsTreeItemRendererV2(){
super();
mouseEnabled = false;
}
override public function set data(value:Object):void{
if(value != null){
super.data = value;
this.itemXml = XML(value);
if(this.itemXml.#checked == "1"){
this.chk.selected = true;
}else{
this.chk.selected = false;
}
}
}
override protected function createChildren():void{
super.createChildren();
chk = new CheckBox();
chk.addEventListener(MouseEvent.CLICK, handleChkClick);
addChild(chk);
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void{
super.updateDisplayList(unscaledWidth,unscaledHeight);
if(super.data){
var tld:TreeListData = TreeListData(super.listData);
//In some cases you only want a checkbox to appear if an
//item is a leaf
//if so, then keep the following block uncommented,
//otherwise you can comment it out to display the checkbox
//for branch nodes
if(tld.hasChildren){
this.chk.visible = true;
}else{
//You HAVE to have the else case to set visible to true
//even though you'd think the default would be visible
//it's an issue with itemrenderers...
this.chk.visible = true;
}
if(chk.visible){
//if the checkbox is visible then
//reposition the controls to make room for checkbox
this.chk.x = super.label.x
super.label.x = this.chk.x + 17;
this.chk.y = super.label.y+8;
}
}
}
private function handleChkClick(evt:MouseEvent):void
{
if(this.chk.selected)
{
this.itemXml.#checked = "1";
}
else
{
this.itemXml.#checked = "0";
}
}
}
}
ComboboxNoClose.as:
package local
{
import flash.events.Event;
import flash.events.MouseEvent;
import mx.controls.CheckBox;
import mx.controls.ComboBox;
import mx.events.DropdownEvent;
import mx.events.ListEvent;
public class ComboBoxNoClose extends ComboBox
{
public function ComboBoxNoClose()
{
super();
}
public function onOpen(event:Event):void
{
event.stopImmediatePropagation();
}
public override function close(trigger:Event = null):void
{
if (trigger != null)
{
super.close();
}
}
}
}
mlb.xml that populates the tree:
<?xml version="1.0" encoding="utf-8"?>
<root>
<league label="American League">
<division label="West">
<team label="Los Angeles" />
<team label="Seattle" />
<team label="Oakland" />
<team label="Texas" />
</division>
<division label="Central">
<team label="Cleveland" />
<team label="Detroit" />
<team label="Minnesota" />
<team label="Chicago" />
<team label="Kansas City" />
</division>
<division label="East">
<team label="Boston" />
<team label="New York" />
<team label="Toronto" />
<team label="Baltimore" />
<team label="Tampa Bay" />
</division>
</league>
</root>
Default behavior of popup is that, when you click on dropdown it closes it self. You can solve this problem like this
<mx:PopUpButton id="popup" width="100%" label="{label}" close="popup_closeHandler(event)" open="popup_openHandler(event)" openAlways="true">
<mx:popUp>
<mx:VBox width="{popup.width*1.25}" mouseEnabled="false" verticalGap="1">
<mx:List id="listSelectAll" width="100%" rowCount="1" selectable="true" itemClick="listSelectAll_itemClickHandler(event)">
<mx:dataProvider>
<mx:Array>
<mx:Object id="selectAll" selected="" label="All"/>
</mx:Array>
</mx:dataProvider>
<mx:itemRenderer>
<mx:Component>
<mx:HBox width="100%" height="100%" mouseChildren="false">
<mx:CheckBox selected="{data.selected}" label="{data.label}" width="100%"/>
</mx:HBox>
</mx:Component>
</mx:itemRenderer>
</mx:List>
<mx:List id="listItems" width="100%" dataProvider="{_dataProvider}" itemClick="listItems_itemClickHandler(event)" variableRowHeight="true">
<mx:itemRenderer>
<mx:Component>
<!--
<mx:HBox width="100%" height="100%" mouseChildren="false" verticalAlign="middle">
<mx:CheckBox selected="{data[outerDocument.selectedField]}" label="{data[outerDocument.labelField]}" width="100%"/>
</mx:HBox>
-->
<mx:HBox width="100%" mouseChildren="false" verticalAlign="middle" horizontalAlign="left" paddingLeft="4">
<mx:Script>
<![CDATA[
override public function set data(value: Object) : void {
super.data = value;
if(data.iconCache == null || outerDocument.cacheIcon == false) {
imgIcon.source = data[outerDocument.iconField];
} else {
imgIcon.source = new Bitmap(data.iconCache);
}
}
protected function image_ioErrorHandler(event:IOErrorEvent):void {
imgIcon.visible = imgIcon.includeInLayout = false;
}
protected function imgIcon_completeHandler(event:Event):void {
imgIcon.visible = imgIcon.includeInLayout = true;
if(outerDocument.cacheIcon) {
var bitmapData:BitmapData = Bitmap(imgIcon.content).bitmapData;
//var bitmap:Bitmap = new Bitmap(bitmapData);
data.iconCache = bitmapData;
//imgIcon.removeEventListener(Event.COMPLETE, imgIcon_completeHandler);
}
}
]]>
</mx:Script>
<mx:CheckBox id="chkSelected" selected="{data[outerDocument.selectedField]}"/>
<mx:Image id="imgIcon" width="{outerDocument.iconWidth}" height="{outerDocument.iconHeight}" visible="{data[outerDocument.iconField]}" includeInLayout="{data[outerDocument.iconField]}" complete="imgIcon_completeHandler(event)" ioError="image_ioErrorHandler(event)"/>
<mx:Label id="lblText" text="{data[outerDocument.labelField]}"/>
</mx:HBox>
</mx:Component>
</mx:itemRenderer>
</mx:List>
</mx:VBox>
</mx:popUp>
</mx:PopUpButton>
or you can visit my post for a complete implementation. here

FLEX: After resizing Image, imageRotate doesn't work ordinarily

Please help me ㅠㅠ
I'm making ImageEditor. but I have a question for Image Rotating function.
When I load image and then Rotate it, Rotate function work very well.
But After resizing(width, height) loaded Image, rotate work ridiculously...
Pixed Image's width, height, It rotate only Image's content..
As a result, Quality of image go down...
This is screenshots.
Oh.. I can't post Images.. because reputation limit... :-<
http://blog.naver.com/hago89n/150164439917
Please visit my blog and confirm screenshots..
Can you understand my problem?
Um.. code is here
//resize button click handler
private function btn_resize_clickHandler(event:MouseEvent):void
{
image.width = parseInt(ti_width.text.toString());
image.height = parseInt(ti_height.text.toString());
}
//Rotate button click handler
private function btn_rotateCCW_clickHandler(event:MouseEvent):void
{//Rotate Counter ClockWise
var o_rotateimage:RotateImage = new RotateImage();
o_rotateimage.rotateCCW(image);
}
//Rotate function in RotateImage.as
public function rotateCCW(image:Image):void
{
m = new Matrix();
m.rotate(Math.PI/2);
m.tx = image.height;
var bd:BitmapData = new BitmapData(image.height as int, image.width as int);
bd.draw(image, m);
image.source = new Bitmap(bd);
}
I think you should do a matrix transformation for both operations.
If you just change the size of your Image object, it does not work fine in all situations.
Here is my code for this example.
//Application
<?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">
<fx:Script>
<![CDATA[
import com.imageeditor.RotateImage;
[Embed(source="/assets/img/chart.png")]
[Bindable]
public var chartPng:Class;
private var o_rotateimage:RotateImage = new RotateImage();
private function btn_resize_clickHandler(event:MouseEvent):void
{
o_rotateimage.scale(image, int(ti_width.text)/image.width, int(ti_height.text)/image.height);
}
private function btn_rotateCCW_clickHandler(event:MouseEvent):void
{
o_rotateimage.rotateCCW(image);
}
]]>
</fx:Script>
<s:VGroup x="10" y="10">
<s:HGroup>
<s:TextInput id="ti_width" text="300" width="40"/>
<s:TextInput id="ti_height" text="200" width="40"/>
<s:Button label="Resize" click="btn_resize_clickHandler(event)"/>
<s:Button label="Rotate" click="btn_rotateCCW_clickHandler(event)"/>
</s:HGroup>
<s:HGroup width="800" height="600" horizontalAlign="center" verticalAlign="middle">
<s:Image id="image" source="{chartPng}" width="600" height="400"/>
</s:HGroup>
</s:VGroup>
</s:Application>
//RotateImage.as
package com.imageeditor
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.geom.Matrix;
import spark.components.Image;
public class RotateImage
{
private var m:Matrix;
public function rotateCCW(image:Image):void
{
var w:int = image.width;
var h:int = image.height;
m = new Matrix();
m.rotate(Math.PI/2);
m.tx = image.height;
var bd:BitmapData = new BitmapData(image.height as int, image.width as int);
bd.draw(image, m);
image.width = h;
image.height = w;
image.source = new Bitmap(bd);
}
public function scale(image:Image, kx:Number, ky:Number):void
{
var w:int = image.width;
var h:int = image.height;
m = new Matrix();
m.scale(kx, ky);
var bd:BitmapData = new BitmapData(w*kx as int, h*ky as int);
bd.draw(image, m);
image.width = w*kx;
image.height = h*ky;
image.source = new Bitmap(bd);
}
}
}

How is an mx:Canvas measured when inside a renderer in Flex 3?

I'm having a sizing issue with a canvases located inside an HBox. It seems "_graphic", "_border" and "_fill" canvases (in com.example.ThingRenderer.mxml) do not get measured at the same time as all the other measurements inside the renderer. However, this problem is only observed on the first pass-through. Refer to the images for a visual... 1st image shows the state of the app after it finished loading. 2nd image represents what the screen looks like after the Populate button is clicked. 3rd image shows what happens when the stepper is incremented. The question is how come the drawing in the 3rd image doesn't get rendered once the data is populated into the table?
RendererTest.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
creationComplete="handleCreationComplete(event)"
>
<mx:Script>
<![CDATA[
import com.example.Thing;
import mx.collections.ArrayCollection;
import mx.events.FlexEvent;
import mx.events.NumericStepperEvent;
private const _thingProvider:ArrayCollection = new ArrayCollection();
private var _thing1:Thing;
protected function handleCreationComplete(event:FlexEvent):void {
_thing1 = new Thing("thingy", 0xff0000, 0.3);
_stepper.value = _thing1.ratio;
}
protected function handlePopulateClick(event:MouseEvent):void {
_thingProvider.addItem(_thing1);
}
protected function handleStepperChange(event:NumericStepperEvent):void {
_thing1.ratio = event.value;
}
]]>
</mx:Script>
<mx:VBox>
<mx:Button label="Populate" click="handlePopulateClick(event)" />
<mx:NumericStepper id="_stepper" minimum="0" maximum="1" stepSize="0.01" change="handleStepperChange(event)" />
<mx:AdvancedDataGrid dataProvider="{_thingProvider}" variableRowHeight="true" width="100%" height="100%">
<mx:columns>
<mx:AdvancedDataGridColumn headerText="Name" dataField="name" />
<mx:AdvancedDataGridColumn headerText="Display"
width="150" sortable="false"
itemRenderer="com.example.ThingRenderer"
/>
</mx:columns>
</mx:AdvancedDataGrid>
</mx:VBox>
</mx:Application>
com.exampleThingRenderer.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas
xmlns:mx="http://www.adobe.com/2006/mxml"
width="100%"
horizontalScrollPolicy="off" verticalScrollPolicy="off"
>
<mx:Script>
<![CDATA[
import mx.binding.utils.ChangeWatcher;
private var _thing:Thing;
private var _ratioWatcher:ChangeWatcher;
private var _doClearContent:Boolean;
private var _doDrawBorder:Boolean;
private var _doUpdateFill:Boolean;
override public function set data(value:Object):void {
if(value && value is Thing) {
_thing = Thing(value);
if(_ratioWatcher) {
_ratioWatcher.unwatch();
}
_ratioWatcher = ChangeWatcher.watch(_thing, "ratio", handleRatioChanged);
_doClearContent = false;
_doDrawBorder = true;
_doUpdateFill = true;
_graphic.invalidateSize();
_border.invalidateSize();
}
else {
_doClearContent = true;
_doDrawBorder = false;
_doUpdateFill = false;
}
super.data = value;
}
private function handleRatioChanged(event:Event):void {
_doUpdateFill = true;
invalidateDisplayList();
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
if(_doClearContent) {
_container.visible = false;
_container.includeInLayout = false;
_doClearContent = false;
}
super.updateDisplayList(unscaledWidth, unscaledHeight);
if(_doDrawBorder) {
trace("_thingContainer.width="+_container.width, "_thingGraphic.width="+_graphic.width, "_thingBorder.width="+_border.width);
_border.graphics.clear();
_border.graphics.moveTo(0, 0);
_border.graphics.lineStyle(1, _thing.color);
_border.graphics.lineTo(_border.width, 0);
_border.graphics.lineTo(_border.width, _border.height);
_border.graphics.lineTo(0, _border.height);
_border.graphics.lineTo(0, 0);
_doDrawBorder = false;
}
if(_doUpdateFill) {
_percentage.text = Math.round(_thing.ratio * 100.0) + "%";
_fill.graphics.clear();
_fill.graphics.beginFill(_thing.color);
_fill.graphics.drawRect(0, 0, _fill.width * _thing.ratio, _fill.height);
_doUpdateFill = false;
}
}
]]>
</mx:Script>
<mx:HBox id="_container" width="100%" paddingLeft="5" paddingTop="5" paddingRight="5" paddingBottom="5">
<mx:Label id="_percentage" width="45" />
<mx:Canvas id="_graphic" width="100%" height="15">
<mx:Canvas id="_border" x="0" y="0" width="100%" height="100%" />
<mx:Canvas id="_fill" x="0" y="0" width="100%" height="100%" />
</mx:Canvas>
</mx:HBox>
</mx:Canvas>
com.example.Thing.as
package com.example {
public class Thing {
[Bindable] public var name:String;
[Bindable] public var color:uint;
[Bindable] public var ratio:Number;
public function Thing(name:String, color:uint, ratio:Number) {
this.name = name;
this.color = color;
this.ratio = ratio;
}
}
}
All this happens because you can't use width and height properties in updateDisplayList, they are not updated yet. Make separate component (e.g. ThingProgressBar) and put drawing logick inside it, that will solve everything:
package
{
import mx.core.UIComponent;
public class ThingProgressBar extends UIComponent
{
private var _ratio:Number;
public function get ratio():Number
{
return _ratio;
}
public function set ratio(value:Number):void
{
_ratio = value;
invalidateDisplayList();
}
override protected function updateDisplayList(
unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
graphics.clear();
if (unscaledWidth > 0 && unscaledHeight > 0)
{
graphics.lineStyle(1, 0xFF0000);
graphics.drawRect(0, 0, unscaledWidth, unscaledHeight);
graphics.beginFill(0xFF0000);
graphics.drawRect(0, 0, unscaledWidth * ratio, unscaledHeight);
graphics.endFill();
}
}
}
}
So your renderer might look like this:
<mx:HBox
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
horizontalScrollPolicy="off" verticalScrollPolicy="off" xmlns:local="*"
>
<fx:Script>
<![CDATA[
[Bindable] private var _thing:Thing;
override public function set data(value:Object):void
{
_thing = value as Thing;
super.data = value;
}
]]>
</fx:Script>
<mx:HBox width="100%"
paddingLeft="5" paddingTop="5"
paddingRight="5" paddingBottom="5">
<mx:Label text="{_thing.name}" width="45" />
<local:ThingProgressBar width="100%" height="15"
ratio="{_thing.ratio}"/>
</mx:HBox>
</mx:HBox>
I removed watcher. Binding by watcher is considered a bad practice, use mxml binding or events instead.
I removed two Canvases with separated border and fill - they can be cobined together.
I used UIComponent instead of Canvas. Don't use containers unless you need layout, they are heavy.
I used HBox instead of Canvas in renderer because I like boxes more :) But you can't avoid using second container in renderer if you need custom styles since List overwrites renderer's stylesheet.

Flex - parent not executing child's function

Many thanks to www.Flextras.com who has been helping me with this question the last couple of days, and I almost have it. I have a main.mxml, child.mxml and headermenu.mxml. I click a button on the headermenu that dispatches an event up to the main.mxml which then executes a method in child.mxml. I know this works because I put an AlertDialog in the function I'm calling inside of the child.mxml. The child.mxml contains a drawingArea object that has an erase(). When I call this directly from child.xml it executes, however if I put drawingArea.erase() inside of the function being called by child's parent (main.mxml) nothing happens.
Here is my code:
Telestrator.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:MobileApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" firstView="views.TelestratorHome" creationComplete="creationCompleteHandler(event)">
<fx:Script>
<![CDATA[
import mx.effects.Fade;
import mx.events.ChildExistenceChangedEvent;
import mx.events.EffectEvent;
import mx.events.FlexEvent;
import mx.events.ResizeEvent;
import mx.managers.PopUpManager;
import qnx.dialog.AlertDialog;
import qnx.events.QNXApplicationEvent;
import qnx.system.QNXApplication;
import views.TelestratorHome;
private var headerTimer:Timer = new Timer(5000,1);
[Bindable] private var headerMenu:HeaderMenu;
[Bindable] private var headerMenuDisplayed:Boolean = false;
private var tele:TelestratorHome;
private var alert2:AlertDialog = new AlertDialog();
protected function creationCompleteHandler(event:FlexEvent):void
{
QNXApplication.qnxApplication.addEventListener(QNXApplicationEvent.SWIPE_DOWN, onApplicationSwipeDown);
headerMenu = new HeaderMenu();
headerMenu.addEventListener(MouseEvent.CLICK, onHeaderMenuClick);
this.tele = new TelestratorHome();
this.tele.width = 100;
this.tele.height = 100;
this.tele.x = 10;
this.tele.y = 10;
this.addChild( tele );
headerMenu.addEventListener('parentDoSomething', onHeaderBarToldMeTo);
headerTimer.addEventListener(TimerEvent.TIMER, onHeaderMenuTimerEvent);
}
protected function onApplicationSwipeDown(event:QNXApplicationEvent) : void
{
showHeaderMenuPopup();
}
private function showHeaderMenuPopup() : void
{
if (!headerMenuDisplayed)
{
headerMenu.x = 0;
headerMenu.y = 0;
PopUpManager.addPopUp(headerMenu,this, false);
headerPopupEffectIn.end();
headerPopupEffectIn.play();
headerMenuDisplayed = true;
}
headerTimer.reset();
headerTimer.start();
}
private function onHeaderMenuTimerEvent(event:TimerEvent) : void
{
hideHeaderMenuPopup();
headerTimer.stop();
}
private function onHeaderMenuClick(event:MouseEvent) : void
{
hideHeaderMenuPopup();
}
private function hideHeaderMenuPopup() : void
{
headerPopupEffectOut.end();
headerPopupEffectOut.play();
headerPopupEffectOut.addEventListener(EffectEvent.EFFECT_END, headerMenuHidden);
}
private function headerMenuHidden(event:EffectEvent) : void
{
PopUpManager.removePopUp(headerMenu);
headerPopupEffectIn.removeEventListener(EffectEvent.EFFECT_END, headerMenuHidden);
headerMenuDisplayed = false;
}
protected function onHeaderBarToldMeTo(event:Event):void{
tele.eraseCanvas(event);
}
]]>
</fx:Script>
<fx:Declarations>
<s:Parallel id="headerPopupEffectIn" target="{headerMenu}">
<s:Move duration="250" yFrom="-75" xTo="0" yTo="0" easer="{sineEaser}" />
</s:Parallel>
<s:Parallel id="headerPopupEffectOut" target="{headerMenu}">
<s:Move duration="250" yFrom="0" xTo="0" yTo="-75" easer="{sineEaser}" />
</s:Parallel>
<s:Sine id="sineEaser" easeInFraction="0.2" />
</fx:Declarations>
</s:MobileApplication>
TelestratorHome.mxml
------------------
<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark"
actionBarVisible="true" creationComplete="init()" overlayControls="false"
tabBarVisible="true" title="Telestrator">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:actionContent>
<s:Button label="Erase" click="drawingArea.erase(event)"/>
<s:Button id="saveBtn" label="Save Image" click="save()" visible="false"/>
</s:actionContent>
<s:titleContent>
<s:Button label="Take Photo" click="takeSnapshot()"/>
</s:titleContent>
<s:Panel id="p" top="1" width="1024" height="600" backgroundAlpha="0.0" horizontalCenter="0">
<s:Panel id="imageViewer" top="-32" width="1024" height="600" backgroundAlpha="0.0" horizontalCenter="0"/>
<s:Panel id="preview" top="-32" width="1024" height="600" backgroundAlpha="0.0" horizontalCenter="0"/>
<DrawingArea xmlns="*" id="drawingArea" y="-32" width="100%" height="509"/>
</s:Panel>
<fx:Script>
<![CDATA[
import com.tinkerlog.WebCam;
import com.tinkerlog.util.Base64;
import flash.media.Camera;
import mx.core.UIComponent;
import mx.graphics.codec.JPEGEncoder;
import mx.graphics.codec.PNGEncoder;
import qnx.dialog.AlertDialog;
private var webCam:WebCam;
private var alert3:AlertDialog = new AlertDialog();
private function init():void {
webCam = new WebCam(1024, 600);
var ref:UIComponent = new UIComponent();
preview.removeAllElements();
preview.addElement(ref);
ref.addChild(webCam);
}
private function takeSnapshot():void {
imageViewer.visible = true;
preview.visible=false;
saveBtn.visible = true;
imageViewer.width = preview.width;
imageViewer.height = preview.height;
var uiComponent : UIComponent = new UIComponent();
uiComponent.width = webCam.width;
uiComponent.height = webCam.height;
var photoData:Bitmap = webCam.getSnapshot();
var photoBitmap:BitmapData = photoData.bitmapData;
uiComponent.addChild(photoData);
imageViewer.removeAllElements();
imageViewer.addElement(uiComponent);
}
private function deleteSnapshot():void {
imageViewer.removeAllElements();
}
public function save():void
{
var bd:BitmapData = new BitmapData(p.width, p.height);
bd.draw(this);
var ba:ByteArray = (new PNGEncoder()).encode(bd);
(new FileReference()).save(ba, "doodle.png");
}
public function eraseCanvas(event:Event):void{
drawingArea.erase(event);
}
]]>
</fx:Script>
</s:View>
HeaderMenu.mxml
--------------
<?xml version="1.0" encoding="utf-8"?>
<s:BorderContainer xmlns:fx="http://ns.adobe.com/mxml/2009" borderVisible="false"
xmlns:s="library://ns.adobe.com/flex/spark" width="1024" height="75" xmlns:mx="library://ns.adobe.com/flex/mx" >
<fx:Script>
<![CDATA[
import mx.managers.PopUpManager;
import qnx.dialog.AlertDialog;
private var tele:Telestrator = new Telestrator();
private var alert:AlertDialog = new AlertDialog();
protected function btn_clickHandler(event:MouseEvent):void
{
// Figure out which button was pressed, and load appropriate view.
}
protected function onButtonInheaderbarClick(event:Event):void{
dispatchEvent(new Event('parentDoSomething'));
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:backgroundFill>
<s:SolidColor alpha="0.9" color="#000000" />
</s:backgroundFill>
<s:layout>
<s:HorizontalLayout horizontalAlign="right" verticalAlign="middle" paddingTop="5" paddingBottom="5" paddingRight="5" paddingLeft="5" gap="5"/>
</s:layout>
<s:Button id="btn_refresh" label="Refresh" height="60" fontSize="18" click="onButtonInheaderbarClick(event)"/>
<s:Label text="APPLICATION NAME" fontSize="32" styleName="FeedItemTitle" textAlign="center" width="100%" height="100%" verticalAlign="middle" />
<s:Button id="btn_about" label="About" height="60" fontSize="18" click="btn_clickHandler(event)"/>
</s:BorderContainer>
OK, still looking it over but the first mistake I see is you nested function which could be causing scoping issues so lets start with that and see if that helps.
Fix your DrawingArea class like so.
package{
import flash.display.BitmapData;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.net.FileReference;
import flash.utils.ByteArray;
import mx.core.UIComponent;
import mx.events.FlexEvent;
import mx.graphics.codec.PNGEncoder;
public class DrawingArea extends UIComponent{
private var isDrawing:Boolean = false;
private var x1:int;
private var y1:int;
private var x2:int;
private var y2:int;
public var drawColor:uint = 0x000000;
public function DrawingArea(){
this.super();
this.addEventListener(Event.ADDED_TO_STAGE, this.erase );
this.addEventListener(MouseEvent.MOUSE_DOWN, this.mouseDown);
this.addEventListener(MouseEvent.MOUSE_MOVE, this.mouseMove);
this.addEventListener(MouseEvent.MOUSE_UP, this.mouseUp);
}
private function mouseDown( e:MouseEvent ):void{
this.x1 = mouseX;
this.y1 = mouseY;
this.isDrawing = true;
}
private function mouseMove(e:MouseEvent ):void{
if (!event.buttonDown){
this.isDrawing = false;
}
this.x2 = mouseX;
this.y2 = mouseY;
if (this.isDrawing){
this.graphics.lineStyle(2, this.drawColor);
this.graphics.moveTo(this.x1, this.y1);
this.graphics.lineTo(this.x2, this.y2);
this.x1 = this.x2;
this.y1 = this.y2;
}
}
private function mouseUp( e:MouseEvent ):void{
this.isDrawing = false;
}
public function erase( e:Event ):void{
this.graphics.clear();
this.graphics.beginFill(0xffffff, 0.00001);
this.graphics.drawRect(0, 0, this.width, this.height);
this.graphics.endFill();
}
}
}
In main.mxml do this
private var child:Child;
function init(){
this.child = = new Child();
this.child.width = 100;
this.child.height = 100;
this.child.x = 10;
this.child.y = 10;
this.addChild( child );
headerMenu.addEventListener('parentDoSomething', onHeaderBarToldMeTo);
}

Find out which tree item has been dropped on to in Flex

I have a mx.List and a mx.Tree. I'm trying to drag items from the list to the tree but instead of have them add to the actually tree itself I just want to find out what the dropTarget on the tree is. The dropTarget variable is always null.
protected function sparkTree_dragDropHandler(event:DragEvent):void
{
var source:* = event.dragInitiator;
var target:* = event.currentTarget;
trace("Dragged......", source.selectedItem.label);
trace("From.........", source.name);
trace("To...........", event.currentTarget.name);
trace("Target Item..", event.currentTarget.dropTarget);
}
Output:
Dragged...... itemOne
From......... _Main_Tree2
To........... _Main_Tree1
Target Item.. null
So just to clarify I'm trying to get the object that the items is being dropped onto.
As a side note using:
var source:List = event.dragInitiator as List;
var target:List = event.currentTarget as List;
resulted in null object reference error. Seems odd?
UPDATE:
(Not sure where the best place to post this is but) I think this may be the solution...
Create custom event: ItemDragEvent
package events
{
import mx.core.DragSource;
import mx.core.IUIComponent;
import mx.events.DragEvent;
public class ItemDragEvent extends DragEvent
{
public static const DRAG_DROP:String = "itemDragDrop";
public static const DRAG_ENTER:String = "itemDragEnter";
public static const DRAG_EXIT:String = "itemDragExit";
public var item:Object;
public var data:Object;
public var index:Object;
public function ItemDragEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=true, dragInitiator:IUIComponent=null, dragSource:DragSource=null, action:String=null, ctrlKey:Boolean=false, altKey:Boolean=false, shiftKey:Boolean=false)
{
super(type, bubbles, cancelable, dragInitiator, dragSource, action, ctrlKey, altKey, shiftKey);
}
}
}
Create an mx.Tree item renderer: TreeItemRenderer
<?xml version="1.0" encoding="utf-8"?>
<s:MXTreeItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
dragEnter="dragEnterHandler(event)"
dragExit="dragExitHandler(event)"
dragDrop="dragDropHandler(event)"
>
<fx:Script>
<![CDATA[
import events.ItemDragEvent;
import mx.core.IUIComponent;
import mx.events.DragEvent;
import mx.managers.DragManager;
protected function dragEnterHandler(event:DragEvent):void
{
DragManager.acceptDragDrop(event.currentTarget as IUIComponent);
var e:ItemDragEvent = new ItemDragEvent(ItemDragEvent.DRAG_ENTER, true);
e.data = data;
e.item = data;
e.index = itemIndex;
dispatchEvent(e);
trace("TreeItemRenderer, dragEnterHandler");
}
protected function dragExitHandler(event:DragEvent):void
{
var e:ItemDragEvent = new ItemDragEvent(ItemDragEvent.DRAG_EXIT, true);
e.data = data;
e.item = data;
e.index = itemIndex;
dispatchEvent(e);
trace("TreeItemRenderer, dragExitHandler");
}
protected function dragDropHandler(event:DragEvent):void
{
DragManager.acceptDragDrop(event.currentTarget as IUIComponent);
var e:ItemDragEvent = new ItemDragEvent(ItemDragEvent.DRAG_DROP, true);
e.data = data;
e.item = data;
e.index = itemIndex;
dispatchEvent(e);
trace("TreeItemRenderer, dragDropHandler");
}
]]>
</fx:Script>
<s:states>
<s:State name="normal" />
<s:State name="hovered" />
<s:State name="selected" />
</s:states>
<s:HGroup left="0" right="0" top="0" bottom="0" verticalAlign="middle">
<s:Rect id="indentationSpacer" width="{treeListData.indent}" percentHeight="100" alpha="0">
<s:fill>
<s:SolidColor color="0xFFFFFF" />
</s:fill>
</s:Rect>
<s:Group id="disclosureGroup">
<s:BitmapImage source="{treeListData.disclosureIcon}" visible="{treeListData.hasChildren}" />
</s:Group>
<s:BitmapImage source="{treeListData.icon}" />
<s:Label id="labelField" text="{treeListData.label}" paddingTop="2"/>
</s:HGroup>
</s:MXTreeItemRenderer>
Finally the Main code
</fx:Declarations>
<fx:Script>
<![CDATA[
import events.ItemDragEvent;
import itemRenderers.TreeItemRenderer;
import mx.collections.ArrayCollection;
import mx.events.DragEvent;
import mx.events.FlexEvent;
import mx.utils.ObjectUtil;
[Bindable]
private var listData:ArrayCollection = new ArrayCollection([ "One", "Two", "Three" ]);
[Bindable]
private var treeData:Object = { label: "First", children: new ArrayCollection([ { label: "Second" }, { label: "Third" } ]) };
protected function windowedapplication1_initializeHandler(event:FlexEvent):void
{
tree1.addEventListener(ItemDragEvent.DRAG_DROP, treeItemDragDropHandler);
}
protected function treeItemDragDropHandler(event:ItemDragEvent):void
{
event.preventDefault();
trace("ItemDragDrop:", ObjectUtil.toString(event.data));
}
]]>
</fx:Script>
<s:layout>
<s:HorizontalLayout horizontalAlign="center" verticalAlign="middle" />
</s:layout>
<mx:Tree id="list1" width="50%" height="100%"
dataProvider="{listData}" labelField="label"
dragEnabled="true"
dragMoveEnabled="true"
dropEnabled="true" />
<mx:Tree id="tree1" width="50%" height="100%"
dataProvider="{treeData}"
dragEnabled="true"
dropEnabled="true"
showRoot="true"
itemRenderer="itemRenderers.TreeItemRenderer" />
Needs a bit of work to sort out visual feedback, but that should output the Tree item that was dropped onto.
In regards to
var source:List = event.dragInitiator as List;
var target:List = event.currentTarget as List;
Try to check your imports. Maybe there was imported Spark lists but you're using MX List? :)
In regards to dropTarget property please refer to documentation about it. It doesn't relate to Flex drag-n-drop functionality. And so there is no surprise the value of this property is null.
What about your problem in general you can use the following snippet which doesn't solve all the problems (I have no time to debug all the aspects) but can provide you some good starting point:
<?xml version="1.0" encoding="utf-8"?>
<s:Application minHeight="600" minWidth="955" xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:s="library://ns.adobe.com/flex/spark">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.collections.ICollectionView;
import mx.events.DragEvent;
import mx.managers.DragManager;
[Bindable]
private var listDataProvider:ArrayCollection = new ArrayCollection([ "First", "Second", "Third" ]);
[Bindable]
private var treeDataProvider:Object =
{ label: "First", children: new ArrayCollection([ { label: "Second" }, { label: "Third" } ]) };
protected function list_dragCompleteHandler(event:DragEvent):void
{
if (event.relatedObject == tree)
{
event.preventDefault();
if (event.action == DragManager.MOVE && list.dragMoveEnabled)
{
var items:Array = event.dragSource.dataForFormat("items") as Array;
var collection:ArrayCollection = list.dataProvider as ArrayCollection;
for each (var item:Object in items)
{
if (collection.contains(item))
collection.removeItemAt(collection.getItemIndex(item));
}
}
}
}
protected function tree_dragDropHandler(event:DragEvent):void
{
var source:List = List(event.dragInitiator);
var target:Tree = Tree(event.currentTarget);
trace("Dragged......", source.selectedItem);
trace("From.........", source.name);
trace("To...........", target.name);
event.preventDefault();
tree.hideDropFeedback(event);
var index:int = tree.calculateDropIndex(event);
var items:Array = new Array();
if (event.dragSource.hasFormat("treeItems"))
items = items.concat(event.dragSource.dataForFormat("treeItems") as Array);
if (event.dragSource.hasFormat("items"))
items = items.concat(event.dragSource.dataForFormat("items") as Array);
if (index > (tree.dataProvider as ICollectionView).length)
index = (tree.dataProvider as ICollectionView).length;
for each (var item:Object in items)
{
var obj:Object = new Object()
obj.label = item;
(tree.dataProvider as ArrayCollection).addItemAt(obj, index);
}
}
protected function tree_dragEnterHandler(event:DragEvent):void
{
if (event.dragInitiator == list)
{
event.preventDefault();
DragManager.acceptDragDrop(event.target as Tree);
tree.showDropFeedback(event);
}
}
protected function tree_dragExitHandler(event:DragEvent):void
{
event.preventDefault();
tree.hideDropFeedback(event);
}
protected function tree_dragOverHandler(event:DragEvent):void
{
if (event.dragInitiator == list)
{
event.preventDefault();
tree.showDropFeedback(event);
}
}
]]>
</fx:Script>
<s:layout>
<s:HorizontalLayout horizontalAlign="center" verticalAlign="middle" />
</s:layout>
<mx:List dataProvider="{listDataProvider}" dragComplete="list_dragCompleteHandler(event)" dragEnabled="true"
dragMoveEnabled="true" dropEnabled="true" id="list" width="200" />
<mx:Tree dataProvider="{treeDataProvider}" dragDrop="tree_dragDropHandler(event)" dragEnabled="true"
dragEnter="tree_dragEnterHandler(event)" dragExit="tree_dragExitHandler(event)"
dragOver="tree_dragOverHandler(event)" dropEnabled="true" id="tree" showRoot="true" width="200" />
</s:Application>

Resources