Can't get animation in Custom layout to work - actionscript-3

I'd like to create a layout that I can animate my items in. So when a dataprovider gets set on my List, the List's layout will then animate those items onto the screen.
So I've created a CustomLayout and added an update() function.
In the updateDisplayList the items are traced out just fine.
But in the update() function where I want to do the animation, the items are tracing out as null even though if I trace layoutTarget's numElement's I get four! If I use a setTimeout after setting the _dataProvider in my Main app of a second or two, then call update() in the ListLayout, then the traces work fine. So my question is, how can I ensure that I call update() to animate the items in when they are actually available??
Main app:
<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:custom="*"
creationComplete="handleCreationComplete()">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
[Bindable]
private var _dataProvider:ArrayCollection;
public function set dataProvider(value:ArrayCollection):void
{
_dataProvider = value;
listLayout.update();
}
private function handleCreationComplete():void
{
var arr:Array = new Array( { label:"1" }, { label:"2" }, { label:"3" }, { label:"4" } );
dataProvider = new ArrayCollection(arr);
}
]]>
</fx:Script>
<s:List id="list"
dataProvider="{ _dataProvider }"
labelField="label">
<s:layout>
<custom:ListLayout id="listLayout" />
</s:layout>
</s:List>
</s:WindowedApplication>
ListLayout:
package
{
import mx.core.ILayoutElement;
import spark.components.supportClasses.GroupBase;
import spark.layouts.supportClasses.LayoutBase;
/**
* ...
* #author
*/
public class ListLayout extends LayoutBase
{
public function update():void
{
var layoutTarget:GroupBase = target;
//layoutTarget.autoLayout = false;
var count:int = layoutTarget.numElements;
trace(count); //traces 4
for (var i:int = 0; i < count; i++)
{
var item:ILayoutElement = useVirtualLayout ? layoutTarget.getVirtualElementAt(i) : layoutTarget.getElementAt(i);
trace(item); //traces null
}
}
override public function updateDisplayList(width:Number, height:Number):void
{
super.updateDisplayList(width, height);
var layoutTarget:GroupBase = target;
var count:int = layoutTarget.numElements;
for (var i:int = 0; i < count; i++)
{
var item:ILayoutElement = useVirtualLayout ? layoutTarget.getVirtualElementAt(i) : layoutTarget.getElementAt(i);
trace(item); //traces out the item
}
}
}
}

Before you call update on your listLayout, you need to validate your "list" to make sure all the elements are available. So add one line list.validateNow() in set dataProvider function as below:
public function set dataProvider(value:ArrayCollection):void
{
_dataProvider = value;
list.validateNow();
listLayout.update();
}

Related

spark Combobox selection is not visible if list changes

I am using a Spark Combobox and a filter function to filter characters in the firstName and lastName. When user types "a" the drop down list is showing all the filtered items with selecting "aram,babu" in the drop down and also showing text as "aram,babu" in the textinput. If the user presses "r", the text input displays "aram,babu", but the drop down list selection disappears. Hitting Enter or clicking the mouse outside the drop down results in the wrong item (last item, i.e. "armu,babu") being selected.
<?xml version="1.0"?>
<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" xmlns:local="*">
<fx:Script><![CDATA[
import mx.collections.ArrayCollection;
private function labelField(item:Object):String {
if (item && item as SalesPerson && item.lastName && item.firstName) {
return item.firstName + "," + item.lastName;
}
return "";
}
private var arr:ArrayCollection;
private var arr1:ArrayCollection;
private var salesPer:SalesPerson;
private function getData():ArrayCollection
{
arr1 = new ArrayCollection();
var salesPerson:SalesPerson = new SalesPerson();
salesPerson.userName = "ravi kumar";
salesPerson.firstName = "ravi";
salesPerson.lastName = "kumar";
var salesPerson1:SalesPerson = new SalesPerson();
salesPerson1.userName = "kiran kumar";
salesPerson1.firstName = "kiran";
salesPerson1.lastName = "kumar";
var salesPerson2:SalesPerson = new SalesPerson();
salesPerson2.userName = "james bond";
salesPerson2.firstName = "james";
salesPerson2.lastName = "bond";
var salesPerson3:SalesPerson = new SalesPerson();
salesPerson3.userName = "ravi babu";
salesPerson3.firstName = "ravi";
salesPerson3.lastName = "babu";
var salesPerson4:SalesPerson = new SalesPerson();
salesPerson4.userName = "rakesh babu";
salesPerson4.firstName = "rakesh";
salesPerson4.lastName = "babu";
var salesPerson5:SalesPerson = new SalesPerson();
salesPerson5.userName = "ramesh babu";
salesPerson5.firstName = "ramesh";
salesPerson5.lastName = "babu";
var salesPerson6:SalesPerson = new SalesPerson();
salesPerson6.userName = "aram babu";
salesPerson6.firstName = "aram";
salesPerson6.lastName = "babu";
var salesPerson7:SalesPerson = new SalesPerson();
salesPerson7.userName = "armu babu";
salesPerson7.firstName = "armu";
salesPerson7.lastName = "babu";
arr1.addItem(salesPerson);
arr1.addItem(salesPerson1);
arr1.addItem(salesPerson2);
arr1.addItem(salesPerson3);
arr1.addItem(salesPerson4);
arr1.addItem(salesPerson5);
arr1.addItem(salesPerson6);
arr1.addItem(salesPerson7);
return arr1;
}
]]></fx:Script>
<s:VGroup width="100%" height="100%">
<local:FilterCombo labelFunction="labelField" dataProvider="{getData()}"/>
</s:VGroup>
</s:Application>
<?xml version="1.0"?>
<s:ComboBox xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" click="clickHandler(event)">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.collections.IList;
import spark.events.TextOperationEvent;
private var unfilteredDataProvider:IList;
override public function set dataProvider(value:IList):void {
super.dataProvider = value;
unfilteredDataProvider = value;
}
override protected function textInput_changeHandler(event:TextOperationEvent):void {
super.textInput_changeHandler(event);
if (unfilteredDataProvider is ArrayCollection) {
ArrayCollection(unfilteredDataProvider).filterFunction = filterMatches;
ArrayCollection(unfilteredDataProvider).refresh();
super.dataProvider = new ArrayCollection(unfilteredDataProvider.toArray());
}
}
protected function filterMatches(item:Object):Boolean {
if (item && item.lastName && item.firstName) {
if (String(item.lastName + item.firstName).toLowerCase().indexOf(textInput.text.slice(0, textInput.selectionAnchorPosition).toLowerCase()) > -1) {
// trace("traderDoFilter true")
return true;
}
}
return false;
}
private function clickHandler(event:MouseEvent):void {
}
]]>
</fx:Script>
</s:ComboBox>
Basically Combobox search is working based on labelfield, you can see the Combobox framework code.
Verify the below mentioned function:
ComboBox -> processInputField(Function)
if (itemMatchingFunction != null)//itemMatchingFunction allways null untill we assign our callback function
matchingItems = itemMatchingFunction(this, textInput.text);
else
matchingItems = findMatchingItems(textInput.text);//calling by default
So we need to assign our custom callbackfunction to itemMatchingFunction(public)
I have made some changes in your code try this, it has some bugs
<?xml version="1.0" encoding="utf-8"?>
<s:ComboBox xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" click="clickHandler(event)">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.collections.IList;
import spark.events.TextOperationEvent;
import spark.utils.LabelUtil;
private var unfilteredDataProvider:IList;
namespace mx_internal;
override public function set dataProvider(value:IList):void {
super.dataProvider = value;
this.itemMatchingFunction = itemMatchingFunction1;
}
override protected function textInput_changeHandler(event:TextOperationEvent):void {
super.textInput_changeHandler(event);
if (dataProvider is ArrayCollection) {
ArrayCollection(dataProvider).filterFunction = filterMatches;
ArrayCollection(dataProvider).refresh();
}
}
protected function filterMatches(item:Object):Boolean {
if (item && item.lastName && item.firstName) {
if (String(item.lastName +","+ item.firstName).toLowerCase().indexOf(textInput.text.slice(0, textInput.selectionAnchorPosition).toLowerCase()) > -1) {
return true;
}
}
return false;
}
/**
* #private
*/
// Returns an array of possible values
private function itemMatchingFunction1(comB:ComboBox,input:String):Vector.<int>
{
// For now, just select the first match
var startIndex:int;
var stopIndex:int;
var retVal:int;
var retVector:Vector.<int> = new Vector.<int>;
retVal = findStringLoop(input, 0, dataProvider.length);
if (retVal != -1)
retVector.push(retVal);
return retVector;
}
/**
* #private
*/
function findStringLoop(str:String, startIndex:int, stopIndex:int):Number
{
// Try to find the item based on the start and stop indices.
for (startIndex; startIndex != stopIndex; startIndex++)
{
var itmStr:String = itemToLabel(dataProvider.getItemAt(startIndex));
itmStr = itmStr.substring(0, str.length);
if (str == itmStr || str.toUpperCase() == itmStr.toUpperCase())
{
return startIndex;
}
}
return -1;
}
/**
* Given a data item, return the correct text a renderer
* should display while taking the <code>labelField</code>
* and <code>labelFunction</code> properties into account.
*
* #param item A data item
*
* #return String representing the text to display for the
* data item in the renderer.
*
* #langversion 3.0
* #playerversion Flash 10
* #playerversion AIR 1.5
* #productversion Flex 4
*/
override public function itemToLabel(item:Object):String
{
if (item && item.lastName && item.firstName)
return item.lastName +","+ item.firstName;
return item[labelField];
}
]]>
</fx:Script>
</s:ComboBox>

Actionscript for remove button element

How can i remove a button in actionscript when we pass that button id.
<fx:Script>
<![CDATA[
public function removebutton(buttonid:String):void
{
hb1.removeElementAt(buttonid);
}
]]>
</fx:Script>
<s:HGroup id="hb1">
</s:HGroup>
As SharpEdge said, there is no public function available to get the element by id, but the work around is:
public function removebutton(buttonid:String):void
{
for (var i:int = 0; i < hb1.numChildren; i++)
{
var object:Object = hb1.getChildAt(i);
if (object.id == buttonid)
{
hb1.removeElementAt(i);
break;
}
}
}
You can use getChildByName(), there is no getElementByID() in Flex.
public function removebutton(buttonid:String):void
{
hb1.removeElement(hb1.getChildByName(buttonid) as IVisualElement);
}

IconItemRenderer with HTML Support

Im currently developing an custom Icon Item Renderer that support HTML in the message fields.
I have two files.
view1.mxml - that contains the spak list component
htmlRenderer.mxml - the icon item renderer
Codes
htmlRenderer.mxml
<?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"
xmlns:mx = "library://ns.adobe.com/flex/mx"
autoDrawBackground = "true"
width = "100%"
creationComplete="callLater(renderHtml, ['test'])">
<fx:Script>
<![CDATA[
import flash.display.Shape;
import mx.core.FlexGlobals;
import mx.core.UIComponent;
import mx.events.FlexEvent;
import spark.components.*;
import spark.components.supportClasses.StyleableTextField;
import spark.primitives.Rect;
public var mainWrapper:VGroup = new VGroup();
public var mainContainer:HGroup = new HGroup();
//Icon Item Renderer
public var iconItemWrapper:HGroup = new HGroup();
public var iconItemImage:Image = new Image();
/* Text elements */
public var iconItemTextGroup:VGroup = new VGroup();
public var iconItemLabel:Label = new Label();
public var iconItemHtmlMessagex:TextArea = new TextArea();
/* Star rating */
public var iconItemRaterGroup:HGroup = new HGroup();
public var iconItemRater:*;
//Decorator
public var decoratorGroup:VGroup = new VGroup();
public var decoratorText:Label = new Label();
public var counterGroup:Rect = new Rect();
public var counterText:Label = new Label();
public var decoratorImage:Image = new Image();
//Icon Item Borders
public var iconItemTopBorder:Rect = new Rect();
public var iconItemBottomBorder:Rect = new Rect();
public var iconItemBackground:Rect = new Rect();
import flash.display.Graphics;
import mx.graphics.LinearGradient;
import mx.graphics.GradientEntry;
protected function drawHeader():void
{
decoratorText.setStyle("fontFamily","Roboto");
decoratorText.setStyle("fontSize","16");
decoratorText.setStyle("fontWeight","bold");
decoratorText.setStyle("color","#ffffff");
decoratorText.setStyle("paddingRight","5");
decoratorText.setStyle("paddingTop","5");
decoratorText.setStyle("paddingLeft","5");
decoratorText.setStyle("paddingBottom","5");
decoratorText.text = data.Location
addElement(decoratorText);
}
protected function renderIconItem():void
{
//main wrapper
iconItemWrapper.paddingBottom=5;
iconItemWrapper.paddingTop=5;
iconItemWrapper.paddingLeft=5;
iconItemWrapper.verticalAlign="top";
addElement(iconItemWrapper);
//icon
iconItemImage.source = data.Image;
if(iconItemImage.sourceWidth > 64){
iconItemImage.width = 64;
iconItemImage.height = 64;
}else{
iconItemImage.width = iconItemImage.sourceWidth;
iconItemImage.height = iconItemImage.sourceHeight;
}
iconItemImage.sourceWidth
iconItemWrapper.addElement(iconItemImage);
iconItemTextGroup.gap = 0;
iconItemTextGroup.paddingBottom=0;
iconItemTextGroup.paddingTop=0;
iconItemTextGroup.verticalAlign="top";
iconItemWrapper.addElement(iconItemTextGroup);
//title
iconItemLabel.setStyle("fontFamily","Roboto");
iconItemLabel.setStyle("fontWeight","bold");
iconItemLabel.setStyle("color","#000000");
iconItemLabel.setStyle("fontSize","16");
iconItemLabel.setStyle("paddingRight","0");
iconItemLabel.setStyle("paddingTop","0");
iconItemLabel.setStyle("paddingLeft","0");
iconItemLabel.setStyle("paddingBottom","0");
iconItemLabel.text = data.Product;
iconItemTextGroup.addElement(iconItemLabel);
//message
iconItemHtmlMessagex.focusEnabled = false;
iconItemHtmlMessagex.selectable = false;
iconItemHtmlMessagex.setStyle("paddingLeft","0");
iconItemHtmlMessagex.setStyle("paddingTop","0");
iconItemHtmlMessagex.setStyle("borderVisible","false");
iconItemHtmlMessagex.setStyle("contentBackgroundAlpha","0");
iconItemTextGroup.addElement(iconItemHtmlMessagex);
renderMessageText();
//iconItemRaterGroup
//iconItemRaterGroup.paddingTop=0;
//iconItemRaterGroup.verticalAlign="bottom";
//iconItemTextGroup.addElement(iconItemRaterGroup);
//decoratorGroup
decoratorGroup.paddingTop=10;
decoratorGroup.verticalAlign="bottom";
iconItemWrapper.addElement(decoratorGroup);
//decoratorText
decoratorText.setStyle("fontFamily","Roboto");
decoratorText.setStyle("fontSize","12");
decoratorText.setStyle("fontWeight","bold");
decoratorText.setStyle("color","#777777");
decoratorText.setStyle("paddingRight","0");
decoratorText.setStyle("paddingTop","0");
decoratorText.setStyle("paddingLeft","0");
decoratorText.setStyle("paddingBottom","0");
decoratorText.text = data.Location
decoratorGroup.addElement(decoratorText);
//decoratorImage
decoratorImage.width = 32;
decoratorImage.height = 32;
decoratorImage.source = "recycle-icon.png";
decoratorImage.sourceHeight
decoratorImage.sourceWidth
decoratorGroup.addElement(decoratorImage);
}
public var myStyleSheet:StyleSheet = new StyleSheet();
private function renderMessageText():void {
var styles:String = "p{ font-size: 11px; }
a{ font-size: 11px; color: #0C81F5; text-decoration: underline; }
a:hover { color: #CCDEF0; text-decoration: underline; }";
myStyleSheet.parseCSS(styles);
StyleableTextField(iconItemHtmlMessagex.textDisplay).htmlText = data.Description2;
}
public function renderHtml(varx:String):void{
setTimeout(renderHtmlTimeout, 1);
}
public function renderHtmlTimeout():void{
StyleableTextField(iconItemHtmlMessagex.textDisplay).styleSheet = myStyleSheet;
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
renderIconItem();
this.graphics.clear();
this.graphics.beginGradientFill(GradientType.LINEAR, [0xffffff,0xefefef], [1,1], [0,255],verticalGradientMatrix(0,0,unscaledWidth, unscaledHeight));
this.graphics.drawRect(0,0,unscaledWidth, unscaledHeight);
}
]]>
</fx:Script>
</s:ItemRenderer>
and view1.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
creationComplete="ini();">
<fx:Declarations>
<s:HTTPService id="xmlDataResource" url="properties.xml"
result="xmlDatasource = xmlDataResource.lastResult.slist.products"/>
</fx:Declarations>
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
[Bindable]
public var xmlDatasource:ArrayCollection;
public function ini():void{
xmlDataResource.send();
}
]]>
</fx:Script>
<s:List id="categoryList" left="0" right="0" top="0" bottom="0" borderAlpha="0.5"
itemRenderer="htmlRenderer"
dataProvider="{xmlDatasource}">
</s:List>
</s:View>
The problem is that when the list loads data, only list items in the view port are rendered and the rest of the information is hidden.
Any help as im in the verge of getting a breakthrough in flex mobile.
below is the screen shot
thank
Basically, the rest is not hidden, it is simply not there :)
The concept of the List-based classes/components in Flex is, the only the visible data is assigned and rendered. If e.g. your list has the height to display 8 items, then it will create 10 items and reuse them. If you scroll up and an items leaves the viewport, it is placed at the bottom of the list and gets new data.
If you want to create all objects and scroll them, try wrapping a VGroup inside a Scroller.

pass parameter in flex NativeProcess

I want to pass two parameter to nativeProcess.
While i am running exe file using windows command with parameter, it is working.
Command for window is "abc.exe a.txt b.txt"
How can I pass two parameters to the exe in that format using flex nativeProcess?
This is what I have so far:
<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" applicationComplete="windowedapplication1_applicationCompleteHandler(event)">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
private var process:NativeProcess;
protected function windowedapplication1_applicationCompleteHandler(event:FlexEvent):void
{
if (NativeProcess.isSupported)
{
callExe();
}
else
{
textReceived.text = "NativeProcess not supported.";
}
}
public function callExe():void
{
var a_FilePath:String = File.applicationStorageDirectory.resolvePath("dist/a.txt").nativePath;
var b_FilePath:File = File.applicationStorageDirectory.resolvePath("dist/b.txt").nativePath;
if (Capabilities.os.toLowerCase().indexOf("win") > -1)
{
var file:File = File.applicationStorageDirectory.resolvePath("dist/abc.exe");
}
var nativeProcessStartupInfo:NativeProcessStartupInfo = new NativeProcessStartupInfo();
// i put the line below and it works in my case
nativeProcessStartupInfo.workingDirectory = File.applicationStorageDirectory.resolvePath();
nativeProcessStartupInfo.executable = file;
var args:Vector.<String> = new Vector.<String>();
args.push(a_FilePath);
args.push(b_FilePath);
nativeProcessStartupInfo.arguments = args;
process = new NativeProcess();
process.start(nativeProcessStartupInfo);
process.addEventListener(ProgressEvent.STANDARD_OUTPUT_DATA, onOutputData);
process.addEventListener(ProgressEvent.STANDARD_INPUT_PROGRESS, inputProgressListener);
process.addEventListener(ProgressEvent.PROGRESS, progressEvent);
process.addEventListener(ProgressEvent.STANDARD_ERROR_DATA, errorData);
}
public function inputProgressListener(event:ProgressEvent):void
{
textReceived.text += "Input Progress Event";
}
public function onOutputData(event:ProgressEvent):void
{
textReceived.text += "Output Event";
}
public function progressEvent(event:ProgressEvent):void
{
textReceived.text += "Progress Event";
}
public function errorData(event:ProgressEvent):void
{
textReceived.text += "Error Event";
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<mx:TextInput id="textReceived" width="352" height="200"/>
</s:WindowedApplication>
I solved my puzzle by adding the line below in bold:
<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" applicationComplete="windowedapplication1_applicationCompleteHandler(event)">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
private var process:NativeProcess;
protected function windowedapplication1_applicationCompleteHandler(event:FlexEvent):void
{
if (NativeProcess.isSupported)
{
callExe();
}
else
{
textReceived.text = "NativeProcess not supported.";
}
}
public function callExe():void
{
var a_FilePath:String = File.applicationStorageDirectory.resolvePath("dist/a.txt").nativePath;
var b_FilePath:File = File.applicationStorageDirectory.resolvePath("dist/b.txt").nativePath;
if (Capabilities.os.toLowerCase().indexOf("win") > -1)
{
var file:File = File.applicationStorageDirectory.resolvePath("dist/abc.exe");
}
var nativeProcessStartupInfo:NativeProcessStartupInfo = new NativeProcessStartupInfo();
// i put the line below and it works in my case
nativeProcessStartupInfo.workingDirectory = File.applicationStorageDirectory.resolvePath();
nativeProcessStartupInfo.executable = file;
var args:Vector.<String> = new Vector.<String>();
args.push(a_FilePath);
args.push(b_FilePath);
nativeProcessStartupInfo.arguments = args;
process = new NativeProcess();
process.start(nativeProcessStartupInfo);
process.addEventListener(ProgressEvent.STANDARD_OUTPUT_DATA, onOutputData);
process.addEventListener(ProgressEvent.STANDARD_INPUT_PROGRESS, inputProgressListener);
process.addEventListener(ProgressEvent.PROGRESS, progressEvent);
process.addEventListener(ProgressEvent.STANDARD_ERROR_DATA, errorData);
}
public function inputProgressListener(event:ProgressEvent):void
{
textReceived.text += "Input Progress Event";
}
public function onOutputData(event:ProgressEvent):void
{
textReceived.text += "Output Event";
}
public function progressEvent(event:ProgressEvent):void
{
textReceived.text += "Progress Event";
}
public function errorData(event:ProgressEvent):void
{
textReceived.text += "Error Event";
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<mx:TextInput id="textReceived" width="352" height="200"/>
</s:WindowedApplication>

How can I get a mx:textarea height to be the same as the content

The initial height of a text area is much larger than the content, I cannot find a way of making it always the same height as the text content:
<mx:TextArea id="textarea" borderStyle="solid" width="100%" wordWrap="true" selectable="false" backgroundAlpha="0" focusAlpha="0" text="this is a little test" />
Gives a bordered box that is much taller than needed.
This also gives an unintential problem if you have links within the content in that a link 'mouseover' is triggered when nowhere near the link.
<mx:Script>
<![CDATA[
public function onInit():void
{
var style:StyleSheet = new StyleSheet();
var aLink:Object = new Object();
aLink.color = "#0000FF";
var aHover:Object = new Object();
aHover.color = "#00FF00";
aHover.textDecoration = "underline";
style.setStyle( "a:hover", aHover );
style.setStyle( "a:link", aLink );
textarea.styleSheet = style;
}
]]>
</mx:Script>
<mx:TextArea id="textarea" width="100%" wordWrap="true" borderStyle="solid" selectable="false" backgroundAlpha="0" focusAlpha="0" >
<mx:htmlText>
<![CDATA[<a href='event:http://www.adobe.com'>Navigate to Adobe.com.</a> this is testing nothing at all really]]>
</mx:htmlText>
</mx:TextArea>
The Text component doesnt suffer from this, but I cannot attach a stylesheet to a text component.
Hoping someone can help. Or is there some other component I can use where I can add a stylesheet to stylise anchor tags.
I found this overridable in the TextArea.as source and if I override it and remove the "2 x" multiplier it almost works but unfortunately it means that the content doesnt get bigger when it needs to and vertically scrolls instead, so its almost there:
override protected function measure():void
{
super.measure();
measuredMinWidth = DEFAULT_MEASURED_MIN_WIDTH;
measuredWidth = DEFAULT_MEASURED_WIDTH;
// TextArea is minimum of two lines of text
measuredMinHeight = measuredHeight = 2 * DEFAULT_MEASURED_MIN_HEIGHT;
}
If you extend Text, you can add a getter/setter that allows you to set the styleSheet of the underlying UITextField object.
package
{
import flash.events.Event;
import flash.text.StyleSheet;
import mx.controls.Text;
import mx.core.mx_internal;
use namespace mx_internal;
public class StyledText extends Text
{
public function StyledText()
{
super();
}
private var _styleSheet:StyleSheet = null;
[Bindable("stylesheetChanged")]
public function get styleSheet():StyleSheet {
return _styleSheet;
}
public function set styleSheet(value:StyleSheet):void {
_styleSheet = value;
if ( textField ) {
textField.styleSheet = _styleSheet;
}
dispatchEvent(new Event("stylesheetChanged"));
}
override protected function createChildren():void {
super.createChildren();
//textField is created in the createChildren
//method of the Label class
if ( textField && styleSheet ) {
textField.styleSheet = _styleSheet;
}
}
}
}
Then you can use the component like so:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:ns1="*" preinitialize="onInit()">
<mx:Script>
<![CDATA[
public function onInit():void
{
var style:StyleSheet = new StyleSheet();
var aLink:Object = new Object();
aLink.color = "#0000FF";
var aHover:Object = new Object();
aHover.color = "#00FF00";
aHover.textDecoration = "underline";
style.setStyle( "a:hover", aHover );
style.setStyle( "a:link", aLink );
text.styleSheet = style;
}
]]>
</mx:Script>
<ns1:StyledText id="text" x="0" y="79">
<ns1:htmlText>
<![CDATA[<a href='event:http://www.adobe.com'>Navigate to Adobe.com.</a> this is testing nothing at all really]]>
</ns1:htmlText>
</ns1:StyledText>
</mx:Application>
I haven't tried what you're attempting, but this link looks like it might help:
http://www.adobe.com/cfusion/communityengine/index.cfm?event=showdetails&postId=13628&productId=2.
Based on Bedwyr's link, I thought I would try recalcing the height myself and this seems to work fine (not noticed any bad side-effects yet, but there may be):
this.addEventListener( Event.CHANGE, onReMeasure );
this.addEventListener( Event.RESIZE, onReMeasure );
override protected function measure():void
{
super.measure();
measuredMinWidth = DEFAULT_MEASURED_MIN_WIDTH;
measuredWidth = DEFAULT_MEASURED_WIDTH;
var lm:TextLineMetrics = getLineMetrics( 0 );
measuredMinHeight = measuredHeight = DEFAULT_MEASURED_MIN_HEIGHT;
}
private function onReMeasure( eventObj:Event ):void
{
var ht:Number = 0;
for( var n:int = 0; n < textField.numLines; n++ )
{
ht += textField.getLineMetrics(n).height;
}
ht += 10; // padding
height = ht;
validateNow();
}