How to make simple actionscript itemrenderer for Flex Mobile? - actionscript-3

I've been making mxml itemRenderers, but from what I hear at adobe, for the Flex Mobile projects they keep saying "make your item renderers with only actionscript3, no mxml"
So...I have this list where im trying to remake the itemRenderer in actionscript the best way I can guess to do so. can some one let me know if im doing something wrong? Maybe I should be doing it in a whole different file..i dont know this is my first time making an all actionscript3 IR.
The text appears, but now my scollToBottom() function no longer works now. I used it with my mxml itemrenderer and it worked fine. so i thought maybe I was doing something wrong here...So this is my primary problem, im assuming something is wrong with how im doing the itemrenderer and thats why the scroll to bottom function wont work anymore.
//my scroll to bottom function that i run after i put something in the list. since its a chat, this way it auto scrolls down for the user to read the latest message.
protected function scrollToBottom():void {
// update the verticalScrollPosition to the end of the List
// virtual layout may require us to validate a few times
var delta:Number = 0;
var count:int = 0;
while (count++ < 10){
chat_list.validateNow();
delta = chat_list.layout.getVerticalScrollPositionDelta(NavigationUnit.END);
chat_list.layout.verticalScrollPosition += delta;
if (delta == 0)
break;
}
}
<?xml version="1.0" encoding="utf-8"?>
<s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" width="100%" height="100%" autoDrawBackground="false" contentBackgroundAlpha=".3" creationComplete="itemrenderer1_creationCompleteHandler(event)">
<fx:Style>
#namespace s "library://ns.adobe.com/flex/spark";
#font-face {
src: url("assets/fonts/mpb.ttf");
fontFamily: "myFont";
embedAsCFF: true;
advancedAntiAliasing: true;
}
</fx:Style>
<fx:Script>
<![CDATA[
import mx.core.FlexGlobals;
import mx.core.UIComponent;
import mx.events.FlexEvent;
import spark.components.Label;
import spark.components.VGroup;
private var msgTxt:Label = new Label();
private var nameLabel:Label = new Label();
private var mainContainer:VGroup = new VGroup();
protected function itemrenderer1_creationCompleteHandler(event:FlexEvent):void
{
maxWidth=this.width;
mainContainer.paddingBottom=10;
mainContainer.paddingTop=10;
mainContainer.verticalAlign="bottom";
mainContainer.explicitWidth=this.width;
this.addElement(mainContainer);
msgTxt.setStyle("fontFamily","myFont");
msgTxt.setStyle("color","#000000");
msgTxt.setStyle("fontSize","35");
msgTxt.setStyle("paddingRight","15");
msgTxt.setStyle("paddingTop","10");
msgTxt.setStyle("paddingLeft","15");
msgTxt.explicitWidth=this.width;
mainContainer.addElement(msgTxt);
nameLabel.setStyle("fontFamily","myFont");
nameLabel.setStyle("color","#666666");
nameLabel.setStyle("paddingLeft","5");
nameLabel.setStyle("fontSize","24");
nameLabel.explicitWidth=this.width;
mainContainer.addElement(nameLabel);
}
override public function set data(value:Object):void {
super.data = value;
if (data == null)
return;
if(data.systemMsg)
{
}
if(data.name)
{
nameLabel.text=data.name;
if(data.name == "You: ")
{
nameLabel.setStyle("textAlign","right");
msgTxt.setStyle("textAlign","right");
nameLabel.setStyle("paddingRight","5");
}
else if(data.name == "Them: ")
{
nameLabel.setStyle("textAlign","left");
msgTxt.setStyle("textAlign","left");
}
else
{
nameLabel.setStyle("textAlign","left");
msgTxt.setStyle("textAlign","left");
}
}
if(data.icon)
{
}
if(data.msg)
{
msgTxt.text=data.msg;
}
}
]]>
</fx:Script>
</s:ItemRenderer>

what you are missing are a few function calls that need to be overwritten so that the size and position of your items are correctly measured at the right point in the work flow. Here is a copy/paste of the code from the default Flex Template.
Also, from the look of things is looks like you are trying to put as3 code into a Flex ItemRenderer, but that isn't going to help you performance wise. You are going to need a pure AS3 class that extends a Class such as LabelItemRenderer
/**
* #private
*
* Override this setter to respond to data changes
*/
override public function set data(value:Object):void
{
super.data = value;
// the data has changed. push these changes down in to the
// subcomponents here
}
/**
* #private
*
* Override this method to create children for your item renderer
*/
override protected function createChildren():void
{
super.createChildren();
// create any additional children for your item renderer here
}
/**
* #private
*
* Override this method to change how the item renderer
* sizes itself. For performance reasons, do not call
* super.measure() unless you need to.
*/
override protected function measure():void
{
super.measure();
// measure all the subcomponents here and set measuredWidth, measuredHeight,
// measuredMinWidth, and measuredMinHeight
}
/**
* #private
*
* Override this method to change how the background is drawn for
* item renderer. For performance reasons, do not call
* super.drawBackground() if you do not need to.
*/
override protected function drawBackground(unscaledWidth:Number,
unscaledHeight:Number):void
{
super.drawBackground(unscaledWidth, unscaledHeight);
// do any drawing for the background of the item renderer here
}
/**
* #private
*
* Override this method to change how the background is drawn for this
* item renderer. For performance reasons, do not call
* super.layoutContents() if you do not need to.
*/
override protected function layoutContents(unscaledWidth:Number,
unscaledHeight:Number):void
{
super.layoutContents(unscaledWidth, unscaledHeight);
// layout all the subcomponents here
}

Related

DataGrid ItemRenderer

Here's the thing: I've this DataGrid that holds a column with the purpose of handle some actions. So, this DataGrid have several data columns and, at the end there is this special column.
At this special column named "operations" there are two icons; one of 'em shows an alarm icon (like a little bell). So, what I've to accomplish is that, initially the alarm icon shows invisible; when the user sets an alarm (through another interface) the alarm icon shows up (with default style color) and when it's time to fire the alarm the alarm icon is supposed to take another style color (like yellow).
So, I've this next definition:
<mx:DataGrid id="dgSomeValues"
dragEnabled="true"
draggableColumns="false"
width="100%" height="100%"
horizontalScrollPolicy="off"
resizableColumns="true"
rowHeight="19">
<components:columns>
<mx:DataGridColumn id="dgcItem" headerText="{resourceManager.getString('resources','columnItem')}" width="70" resizable="false"/>
<!--
Some other dataGridColumns
-->
<mx:DataGridColumn id="dgcOperation" headerText=" " width="50" resizable="false">
<mx:itemRenderer>
<fx:Component>
<components:OperationItemRenderer/>
</fx:Component>
</mx:itemRenderer>
</mx:DataGridColumn>
</components:columns>
And definition for OperationItemRenderer is like this:
import flash.events.MouseEvent;
import com.broker.control.BrokerEvent;
import mx.containers.HBox;
public class OperationItemRenderer extends HBox
{
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
/**
* Alarm Button --> bell
**/
private var btnAlarm:Icon;
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor
**/
public function OperationItemRenderer()
{
super();
this.setStyle("horizontalGap",0);
}
//--------------------------------------------------------------------------
//
// Overridden methods
//
//--------------------------------------------------------------------------
/**
* #inheritDoc
**/
override public function set data(value:Object):void
{
super.data = value;
}
/**
* #inheritDoc
**/
protected override function createChildren():void
{
super.createChildren();
if (!btnAlarm){
btnAlarm = new Icon();
btnAlarm.styleName = ""; // Initially do not shows any icon.
btnAlarm.enabled = true;
btnAlarm.addEventListener(MouseEvent.CLICK, btnAlarmClickHandler,false,0,true);
addChild(btnAlarma);
}
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
if (btnAlarm){
btnAlarm.width = unscaledWidth/2;
btnAlarm.height= unscaledHeight;
}
}
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
/**
* If this item has an alarm, then it will show an icon.
* States for alarm icon are: Default --> icnAlarmOff
* Fired --> icnAlarm
*/
public function upgradeIcon(toogle:Boolean):void
{
btnAlarm.styleName = toogle ? "icnAlarm" : "icnAlarmOff";
}
//--------------------------------------------------------------------------
//
// Event Handlers
//
//--------------------------------------------------------------------------
protected function btnAlarmaClickHandler(event:MouseEvent):void
{
if (btnAlarm.styleName == "favIcnAlarma") {
var evt:BrokerEvent;
evt = new BrokerEvent(BrokerEvent.SETUP_ALARM);
dispatchEvent(evt);
}
}
}
The function "upgradeIcon" it's supposed to be called from another section in the application where a rowFunction it's called everytime the DataGrid's datasource is refreshed. At this rowFunction I want to find a way to access DataGrid ItemRenderer so that way I'll be able to call this upgradeIcon function.
The question is, how can I access DataGrid's ItemRenderer programatically? What I already tried is something like this:
var c:ClassFactory = view.dgcOperation.itemRenderer as ClassFactory;
if (c != null && c.generator != null) { // c never is null at this point
var f:OperationItemRenderer = c.generator as OperationItemRenderer;
f.upgradeIcon(change); // f it's always null
//(c.generator as OperationItemRenderer).upgradeIcon(change);
}
But this approach does not work. Any help would be greatfully appreciated.
Thanks in advance!
try to dispatch an event when user set alarm...and add a listener of this event in your renderer, so when a new alarm is set, an event will be dispatched from your alarm interface, and be captured in your renderer which will update your icon style.
Your datagrid must be having a data provider of value objects (say Report is name of your class) . If you have marked your Report class bindable then any change in any of its property will dispatch PropertyChangeEvent and then you can override set Data method in the item-renderer and based on the value of that property you should have taken necessary action.
ItemRenderers are recycled and fetching them in this was is a bad practice. You should not alter an ItemRenderer directly rather alter the underlying object.

Update spark List control on dataProvider change

I am using spark List control which is bind to an instance of such class:
[Event(name="collectionChange", type="mx.events.CollectionEvent")]
public class HierarchicalCollectionListAdapter extends EventDispatcher implements IList
{
...
}
I want to make List fully re-draw each time when this collection send "reset" collectionChange event. Now to achieve this, each time after collection update I have to call this code:
var _itemRenderer:IFactory = _list.itemRenderer;
_list.itemRenderer = null;
_list.itemRenderer = _itemRenderer;
Is there any way to do it in more elegant way?
It's a bit hard for me to see whats going on here but it looks like your list dataProvider is not extending ListCollectionView so you can't use refresh()
However, you may be able to force a redraw using invalidateDisplayList()
I would try something like this:
_list.dataGroup.invalidateDisplayList();
I'll leave my solution here in case if someone will have same problem.
Best thing that I could do - create extended list control, which will refresh itself each time on collection update. So, create new control, based on spark list and add this code there:
<fx:Script>
<![CDATA[
import mx.collections.IList;
import mx.events.CollectionEvent;
import mx.events.CollectionEventKind;
/**
* Subscribe to collection change event.
*/
override public function set dataProvider(value:IList):void
{
if (dataProvider)
dataProvider.removeEventListener(CollectionEvent.COLLECTION_CHANGE,
dataProvider_collectionChangeHandler);
if (value)
value.addEventListener(CollectionEvent.COLLECTION_CHANGE, _collectionChangeHandler, false, 0, true);
var _itemRenderer:IFactory = this.itemRenderer;
this.itemRenderer = null;
this.itemRenderer = _itemRenderer;
super.dataProvider = value;
}
/**
* If collection has changed - redraw list by resetting itemRenderer.
*/
private function _collectionChangeHandler(event:Event):void
{
if (event is CollectionEvent)
{
var ce:CollectionEvent = CollectionEvent(event);
// We don't need to refresh if any collection element will change.
if(ce.kind != CollectionEventKind.UPDATE)
{
var _itemRenderer:IFactory = this.itemRenderer;
this.itemRenderer = null;
this.itemRenderer = _itemRenderer;
}
}
}
]]>
</fx:Script>

Reduce lifecycle validation calls in resizeable Flex LabelItemRenderer

I've subclassed the LabelItemRenderer class to create an expandable renderer for a spark list in my mobile app.
When the user selects an item, the renderer's size increases, additional data is shown. The renderer basically looks like this (I've removed the parts that don't matter here, so this is basically pseudo code).
public class PositionsGridRenderer extends LabelItemRenderer
{
public function PositionsGridRenderer() {
super();
addEventListener(MouseEvent.CLICK, expandHandler);
}
protected override function createChildren():void {
super.createChildren();
_dg = new DataGroup();
_dg.visible = false;
addChild(_dg);
}
private function expandHandler(event:Event):void {
if(_gridVisible) {
if(!_detailClicked) {
_dg.visible = false;
_gridVisible = false;
}
_detailClicked = false;
} else {
_dg.visible = true;
_gridVisible = true;
}
}
public override function set data(value:Object):void {
if(!value) return;
super.data = value;
var pos:Position = data as Position;
label = pos.positionName;
_dg.dataProvider = pos.positionSymbols;
}
protected override function measure():void {
!_gridVisible ? measuredHeight = 30 : measuredHeight = 30 + getElementPreferredHeight(_dg);
this.height = measuredHeight;
}
protected override function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void {
setElementSize(labelDisplay, unscaledWidth, 30);
setElementPosition(labelDisplay, 10,10);
if(_gridVisible) {
setElementSize(_dg, unscaledWidth, getElementPreferredHeight(_dg));
setElementPosition(_dg, 0, 30);
} else {
setElementSize(_dg, unscaledWidth, 0);
}
invalidateSize();
}
}
}
Works as expected, I'm just wondering if there's a way to reduce the amount of validation calls this renderer does when I expand it.
If it is clicked to be expanded the layoutContents and measure functions are both called three times in the following order: layoutcontents -> measure, layoutcontens -> measure, layoutcontents -> measure.
I'd understand them being called once because I invalidate the size, but three times seems odd.
Does anyone know why this is happening, or maybe even how to prevent this from happening?
The real question was why is the component going through three full renderer cycles? After some disussion, this is what we came across:
The first time the invalidate cycle is triggered is when a mouse down, or possibly a touch begin event occurs. This puts the component into the hover state; which causes a visual change in the component.
The second time the invalidate cycle is triggered is when the item is selected. This puts the renderer in the down state; causing a different visual indicator to be drawn.
The third invalidate cycle is caused by the component's own code; when layoutContents() calls invalidatesize()

How to keep the ColorPicker swatch open all the time with Flash AS3?

I want the ColorPicker popup swatch to always stay open.
var cp:ColorPicker = new ColorPicker();
cp.open();
works fine but when focus is lost the window closes.
Any suggestions?
Thanks
The flash default color picker
Depends on which ColorPicker you are using. ( i mean who wrote it ).
But this one works as you need: http://www.bit-101.com/blog/?p=2347
Update
Then you need to create you own ColorPicker class which will extend the original ColorPicker:
package
{
import fl.controls.ColorPicker;
import flash.events.MouseEvent;
/**
* ...
* #author Jevgenij Dmitrijev ( http://www.ifmi.lt )
*
* #created {2012.05.10 16:08}
*
*/
public class CustomColorPicker extends ColorPicker
{
var _allowHide:Boolean = false;
public function CustomColorPicker()
{
}
override protected function onStageClick(event:MouseEvent):void
{
//Simple example .
if(_allowHide)
super.onStageClick(event);
}
override protected function onSwatchClick(event:MouseEvent):void
{
// since on click it is closing, ovveride the function
// and super the over function, since it is the one
// which changes the color.
super.onSwatchOver(event)
}
override protected function onSwatchOver(event:MouseEvent):void
{
// just ovveride it, so it would do nothing.
}
}
}
And then in you project use:
var colorPickerMC:CustomColorPicker = new CustomColorPicker ();
addChild(colorPickerMC);

How do I call a method when a public property changes in Flex 3?

If I have a .mxml file that has a method in it and a public property, can I have the method execute whenever the property changes.
<?xml version="1.0" encoding="utf-8"?>
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
[Bindable]public var myProperty:MyType;
private function myMethod(myProperty):void
{
// Method to run every time myProperty changes
}
]]>
</mx:Script>
</mx:HBox>
In another .mxml file I have added this .mxml file like so:
<viewComponents:MyViewComponent myProperty="{myVariable}" />
Since no one said this yet, I'll propose a second approach.
Every property in the Flex Framework will dispatch a property named something like *property*Changed. where property is the name of the property to be changed. Said properties are implemented using get set methods as others have mentioned. Something like this:
private var _myProperty:MyType;
[Bindable(myPropertyChanged)]
public function get myProperty():MyType
{
return _myProperty;
}
public function set myProperty(value:MyType):void
{
_myProperty = value;
dispatchEvent(new Event('myPropertyChanged'));
}
These event name specified in the Bindable metadata is used for binding purposes. So, instead of calling your method inside the set, you could listen for this myPropertyChanged event:
component.addEventListener('myPropertyChanged',onMyPropertyChanged)
And elsewhere in the code:
protected function onMyPropertyChanged(event:Event):void{
// do other processing
}
This may be overkill for what you're trying to accomplish; or not. Since you didn't go into specifics on what you were trying to accomplish, I'm not sure.
If your new functionality relates to the Flex Component LifeCycle in some manner, such as changing the display or the size you should be performing your changes in the lifecycle ethods; not in your set method. Something like this:
private var _myProperty:MyType;
private var _myPropertyChanged:Boolean = false
[Bindable('myPropertyChanged')]
public function get myProperty():MyType
{
return _myProperty;
}
public function set myProperty(value:MyType):void
{
_myProperty = value;
_myPropertyChanged = true;
invalidateProperties();
invalidateDisplayList();
invalidateSize()
invalidateSkinState(); // spark comps only
dispatchEvent(new Event('myPropertyChanged'));
}
The invalidate methods will force the component lifecycle method to rerun during the next render event and you can use code like this in the relevant method:
if(_myPropertyChanged == true){
_myPropertyChanged = false;
// do other processing
}
You can use get and set accessor methods. More details is here.
In you case it is something like:
private var _myProperty:MyType;
public function set myProperty(value:MyType):void
{
_myProperty = value;
// he best way is to place myMethod body here
myMethod(_myProperty);
}
[Bindable]
public function get myProperty():MyType
{
return _myProperty;
}
This is how I would do it. Create a setter function that calls the method you propose:
var _mystatus:Number = 0;
function set mystatus(val:Number):void
{
_mystatus = val;
alertfunction();
}
function get mystatus():Number
{
return _mystatus;
}