I have prepared a simple test case for a PopUpButton opening a TileList with black and red entries and it mostly works, but has 2 annoyances.
I've searched a lot, tried several variants (added [Bindable] members in my renderer; added color member to the bids array; created my public override set data() method; ...) and has been getting some answers too, but they are way too general.
I would appreciate if someone can suggest code to fix the 2 issues in my code:
1) Scrolling "tl2" right-left doesn't work well: the entries are displayed in a mix of red and black. I know the TileList reuses itemRenderer, but how do I fix the problem?
2) In debug-mode I get numerous warnings:
warning: unable to bind to property 'label' on class 'Object' (class is not an IEventDispatcher)
Thank you,
Alex
MyRenderer.mxml:
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml"
verticalScrollPolicy="off" horizontalScrollPolicy="off"
width="100%" height="100%">
<mx:Script>
<![CDATA[
public static function findColor(str:String):uint {
return (str.indexOf('♥') != -1 ||
str.indexOf('♦') != -1) ? 0xFF0000 : 0x000000;
}
]]>
</mx:Script>
<mx:Label truncateToFit="true" width="60"
text="{data.label}" color="{findColor(data.label)}"/>
</mx:Canvas>
MyTest.mxml:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
creationPolicy="all" applicationComplete="init(event);">
<mx:Style>
#font-face {
src:url("C:\\WINDOWS\\Fonts\\arial.ttf");
fontFamily: myFont;
unicodeRange:
U+0020-U+0040, /* Punctuation, Numbers */
U+0041-U+005A, /* Upper-Case A-Z */
U+005B-U+0060, /* Punctuation and Symbols */
U+0061-U+007A, /* Lower-Case a-z */
U+007B-U+007E, /* Punctuation and Symbols */
U+0410-U+0451, /* cyrillic */
U+2660-U+266B; /* card suits */
}
List, CheckBox, Label, Button, PopUpButton, TileList {
fontFamily: myFont;
fontSize: 24;
}
</mx:Style>
<mx:Script>
<![CDATA[
import mx.controls.*;
import mx.events.*;
[Bindable]
private var bids:Array;
private var tl:TileList;
private function init(event:FlexEvent):void {
bids = createBids();
pub.popUp = createList(bids);
}
private function createBids():Array {
var arr:Array = [{label: 'Pass'}];
for (var i:uint = 6; i <= 10; i++)
for (var j:uint = 0; j < 5; j++)
arr.unshift({label: i+'♠♣♦♥ '.charAt(j%5)});
return arr;
}
private function createList(arr:Array):TileList {
tl = new TileList();
tl.maxColumns = 5;
tl.width = 350;
tl.height = 250;
tl.dataProvider = arr;
tl.itemRenderer = new ClassFactory(MyRenderer);
tl.addEventListener('itemClick', itemClickHandler);
if (arr.length > 0) {
tl.selectedIndex = arr.length - 1;
pub.label = arr[tl.selectedIndex].label;
}
return tl;
}
private function itemClickHandler(event:ListEvent):void {
var index:uint = tl.columnCount * event.rowIndex + event.columnIndex;
var label:String = bids[index].label;
pub.label = label;
pub.setStyle('color', MyRenderer.findColor(label));
pub.close();
tl.selectedIndex = index;
}
]]>
</mx:Script>
<mx:Panel title="TileList scrolling problem" height="100%" width="100%"
paddingTop="10" paddingBottom="10" paddingLeft="10" paddingRight="10">
<mx:Label width="100%" color="blue" text="Select your bid:"/>
<mx:TileList id="tl2" height="200" width="200"
maxColumns="5" rowHeight="30" columnWidth="60"
dataProvider="{bids}" itemRenderer="MyRenderer"/>
</mx:Panel>
<mx:ApplicationControlBar width="100%">
<mx:Spacer width="100%"/>
<mx:CheckBox id="auto" label="Auto:"/>
<mx:Button id="left" label="<<"/>
<mx:PopUpButton id="pub" width="90"/>
<mx:Button id="right" label=">>"/>
</mx:ApplicationControlBar>
</mx:Application>
Update:
Thank you Wade, the warning is gone now (I guess it was not ok to use {data.label} in my label), but the "tl2" still has scrolling issues.
New MyRenderer.mxml (still has scrolling issues):
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml"
verticalScrollPolicy="off" horizontalScrollPolicy="off"
width="100%" height="100%">
<mx:Script>
<![CDATA[
override public function set data(value:Object):void {
super.data = value;
var str:String = String(value.label);
myLabel.text = str;
myLabel.setStyle('color', findColor(str));
}
public static function findColor(str:String):uint {
return (str.indexOf('♥') != -1 ||
str.indexOf('♦') != -1) ? 0xFF0000 : 0x000000;
}
]]>
</mx:Script>
<mx:Label id="myLabel" truncateToFit="true" width="60"/>
</mx:Canvas>
You can take care of both of your issues by overriding the set data method on your item renderer:
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml"
verticalScrollPolicy="off" horizontalScrollPolicy="off"
width="100%" height="100%">
<mx:Script>
<![CDATA[
override public function set data(value:Object):void {
super.data = value;
var str:String = value.label;
this.myLabel.text = str;
this.myLabel.setStyle("color", (str.indexOf('♥') != -1 ||
str.indexOf('♦') != -1) ? 0xFF0000 : 0x000000);
}
]]>
</mx:Script>
<mx:Label id="myLabel" truncateToFit="true" width="60"/>
</mx:Canvas>
Since the renderers are re-used, the best way to ensure they are correctly updated is to use the set data method since it always gets called when a renderer gets re-used. This also gets rid of your binding warning since you are no longer binding to data.label. Note: I haven't tested this code, it may need some tweaking :) Hope that helps.
EDIT: Your "tl2" issue looks like it's caused by horizontally scrolling your tile list, whereas the TileList appears to be optimized for vertical scrolling. Since your data set is finite and relatively small, I would make the tile list full size to show all of the elements (eliminating item renderer re-use) and wrap it in a canvas set to the desired dimensions and let the canvas handle the scrolling. Probably not the answer you are looking for, sorry.
Related
My 1st question here...
Spark component TextArea does have a gestureZoom event property, but it seems that it has no functionality?
I would like to implement a simple zoom feature in my TextArea, which simply increases or decreases fontSize property, making text appear larger or smaller.
After implementing all the necessary code (and it works if instead of TextArea I use Image), pinch&zoom does not trigger the gestureZoom event on TextArea object.
Any suggestions? (I don't insist on using pinch&zoom, it just seems appropriate...)
Here 's the code:
<?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"
applicationComplete="application1_applicationCompleteHandler(event)">
<fx:Script>
<![CDATA[
import mx.binding.utils.BindingUtils;
import mx.events.FlexEvent;
[Bindable]
public var currFontSize:int = 24;
protected function application1_applicationCompleteHandler(event:FlexEvent):void {
Multitouch.inputMode = MultitouchInputMode.GESTURE;
if(Multitouch.supportsGestureEvents){
txtbox.addEventListener(TransformGestureEvent.GESTURE_ZOOM, onGestureZoom);
} else {
status.text="gestures not supported";
}
}
// THIS NEVER GETS CALLED?
private function onGestureZoom( event : TransformGestureEvent ) : void {
info.text = "event = " + event.type + "\n" +
"scaleX = " + event.scaleX + "\n" +
"scaleY = " + event.scaleY;
// Zomm in/out simply by increasing/decreasing font size
if (event.scaleX <1 || event.scaleY <1){
if (currFontSize > 12) currFontSize -=2;
}
else{
if (currFontSize < 64) currFontSize +=2;
}
}
protected function button1_clickHandler(event:MouseEvent):void {
info.text = "";
currFontSize = 24;
}
]]>
</fx:Script>
<s:Label id="status" top="10" width="100%" text="Transform Gestures on TextArea"
textAlign="center"/>
<s:HGroup left="12" right="12" top="40">
<s:TextArea id="info" width="100%" height="117" editable="false"/>
<s:Button label="Reset" click="button1_clickHandler(event)"/>
</s:HGroup>
<s:TextArea id="txtbox" left="12" right="12" bottom="12" height="400"
fontSize="{currFontSize}"
gestureZoom="onGestureZoom(event)"
text="Here is some sample text I want enlarged or shrunk."/>
</s:Application>
If the TextArea doesn't need to be editable.. see if you can use a Label. that should work with the pinch and zoom.
I've got datagrid with custom itemrenderer. I want to play animatecolor effect for a cell in which data was changed. I don't want to play that effect for all cells.
Here is code of itemrenderer:
<?xml version="1.0" encoding="utf-8"?>
<s:GridItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" clipAndEnableScrolling="true">
<fx:Declarations>
<s:Sequence id = "updateEffect">
<s:AnimateColor colorFrom ="0xffffff"
colorTo ="0xb9d30d"
target = "{lblData}"
duration ="5000"/>
<s:AnimateColor colorFrom ="0xb9d30d"
colorTo ="0xffffff"
target = "{lblData}"
duration ="5000"/>
</s:Sequence>
</fx:Declarations>
<fx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.events.FlexEvent;
override public function prepare(hasBeenRecycled:Boolean):void {
if(data)
{
lblData.text = data[column.dataField];
if(data.selected == true)
{
lblData.setStyle("color","#61afda");
lblData.setStyle("textAlign","center");
lblData.setStyle("fontWeight","bold");
lblData.setStyle("fontSize","19");
lblData.setStyle("paddingLeft","10");
lblData.setStyle("paddingRight","10");
lblData.setStyle("paddingBottom","0");
lblData.setStyle("paddingTop","2");
containter.setStyle("backgroundAlpha","1.0");
containter.setStyle("backgroundColor","0x2f3437");
}
else
{
lblData.setStyle("color","#d4d4d4");
lblData.setStyle("textAlign","center");
lblData.setStyle("fontWeight","bold");
lblData.setStyle("fontSize","12");
lblData.setStyle("paddingLeft","10");
lblData.setStyle("paddingRight","10");
lblData.setStyle("paddingBottom","5");
lblData.setStyle("paddingTop","4");
containter.setStyle("backgroundAlpha","0.0");
updateEffect.play();
}
}
}
]]>
</fx:Script>
<s:SkinnableContainer
id ="containter"
width ="100%"
height ="100%">
<s:HGroup
width ="100%"
height ="100%">
<s:Label
id ="lblData"
width ="100%"
height ="100%"
maxDisplayedLines ="1"
styleName ="FPlayGcItemRenderStyle"/>
</s:HGroup>
</s:SkinnableContainer>
Any suggestions?
I don't know if its the best way but I generally add a listener to my data when I need to update properties dynamically in my item renderers.
//This is inside an item renderer
private var _myClass:FunkyClass;
override protected function set data( value:Object ):void
{
super.data = value;
if( value != null )
{
if( _myClass != null )
{
_myClass.removeEventListener(
'MyFunkyClass.DataChange' , updateSomething);
}
_myClass = value as FunkyClass;
_myClass.addEventListener(
'MyFunkyClass.DataChange' , updateSomething);
}
}
private function updateSomething( event:Event ):void
{
//change colors, visibility and so forth
}
Of course your data class has to extend EventDispatcher. I would also be interested in seeing how other folks in the community have solved this problem.
i have the following code to play a video stored in the media server:
<?xml version="1.0" encoding="utf-8"?>
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
width="592" height="372">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import mx.events.ResizeEvent;
private var ns:NetStream;
private var nc:NetConnection;
private var video:Video;
private var meta:Object;
private function ns_onMetaData(item:Object):void {
trace("meta");
meta = item;
// Resize Video object to same size as meta data.
video.width = item.width;
video.height = item.height;
// Resize UIComponent to same size as Video object.
myVid.width = video.width;
myVid.height = video.height;
}
private function fetch_rec():void {
var nsClient:Object = {};
nsClient.onMetaData = ns_onMetaData;
nc = new NetConnection();
nc.connect(null);
ns = new NetStream(nc);
ns.client = nsClient;
video = new Video();
video.attachNetStream(ns);
video.smoothing=true;
myVid.addChild(video);
ns.play("http://localhost:5080/oflaDemo/recordings/firstattempt.flv");
}
]]>
</fx:Script>
<mx:VideoDisplay id="myVid" bottom="0" width="100%" height="100%"
/*??how to make the progressbar work???*/ playheadUpdate="progBar.setProgress(myVid.playheadTime,myVid.totalTime)"/>
<s:HGroup right="10" top="7">
<mx:Button id="button4" x="498" y="250" label="Play/Reload" click="fetch_rec();"/>
<mx:Button id="button5" x="512" y="279" label="Pause" click="ns.pause()"/>
</s:HGroup>
<mx:ProgressBar id="progBar" left="10" right="10" bottom="7" height="10" label="" mode="manual"/>
</s:Group>
and i would like to know
1)how to make the video follow the resizing of the myVid VideoDisplay and does not show small inside it as it does and
2)how to make the progressbar work with the streamed video...
Thanks in advance for your help!
I can't answer for progress bar. For scaling video to video display I use Transform and Matrix, when VideoDisplay size is changed I just replace old Matrix with new scaled one. If your video will be smaller then target then it should expand.
Here are snippets of code (I skip creation, etc):
<!-- language: lang-xml -->
protected function scaleVideoToDisplay():void {
var ratio:Number;
if (video == null) {
return;
}else if (video.width == 0 || video.height == 0) {
ratio = 0.0;
}else {
ratio = Math.min(
videoDisplay.width / video.width,
videoDisplay.height / video.height);
}
video.transform.matrix = new Matrix(ratio, 0, 0, ratio);
}
<s:VideoDisplay width="100%" height="100%" resize="scaleVideoToDisplay()"/>
<!-- langugae:lang-none -->
For video creation I use:
<!-- language: lang-xml -->
video.transform = new Transform(video);
In addition to this You will probably need to scale video after creation.
First of all I know there is a spark VolumeBar component but, for design requirements I can't use it..
I'm trying to create a custom component but heights are not responding as should
[Update]
This is were I call the class
<components:VolumeSlider steps="4" height="100" />
The problem is that the volume slider is adapting perfectly, but My custom items component doesn't.
<s:HGroup width="100%" height="100%" maxHeight="{height}" >
<s:VGroup width="100%" height="100%" paddingBottom="20" paddingTop="20">
<s:VSlider id="slider" width="100%" height="100%" maximum="{_steps-1}" />
</s:VGroup>
<s:VGroup id="items" width="100%" height="100%" />
</s:HGroup>
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
[Bindable]
private var _steps:uint = 10;
public function set steps( value:uint ):void
{
_steps = value;
if ( items != null && items.numChildren != 0 )
{
items.removeAllElements();
}
create();
}
private function create():void
{
for ( var i:uint = 0; i < _steps; ++i )
{
var item:VolumeSliderItem = new VolumeSliderItem();
item.percentHeight = item.percentWidth = 100;
if ( items != null )items.addElement(item );
}
}
]]>
</fx:Script>
where VolumeSliderItem is a spark button
I don't see any call to create(). I added 'creationComplete="create()"' on the Application tag, and then it created 10 sliders to the VGroup with id 'items'. Is that what you're looking for?
I have a custom component that is basically a VBox with a Label and a TextField.
<mx:VBox width="50%">
<mx:Label width="100%"/>
<mx:TextField width="100%"/>
</mx:VBox>
What I want, basically, is to have two of these VBoxes layed out on a HBox and each would take exactly 50% - their children Label and TextField should just obey that.
If I set both Label and TextField's width to 100%, and the Label text doesn't fit, the default behaviour is to expands the VBox width - I don't want that to happen. The TextField should always take 100% of the width, and I'd want the Label to be explicitly set to the width of the TextField, so the text would be truncated and not expand the VBox width.
Is there a way to tell the Label to just obey the VBox (or TextField) width and not be included in the measurement of the VBox width?
Not sure if I was clear. :)
Thanks in advance.
it wasn't that easy as I thought. At the beginning I wanted to suggest maxWidth but it doesn't work correctly with label. However I just tested this:
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import mx.controls.TextInput;
import mx.controls.Label;
private function onTextChanged(event:Event):void {
var currentText:String = TextInput(event.target).text;
var shortened:Boolean = false;
// adding 30 to leave some space for ...
while ( lbl.measureText(currentText).width > (lbl.width-30) ) {
currentText = currentText.substr(0,currentText.length-1);
shortened = true;
}
lbl.text = currentText + ((shortened) ? "..." : "" );
}
]]>
</mx:Script>
<mx:VBox width="50%">
<mx:Label id="lbl" width="100%" />
<mx:TextInput width="100%" change="onTextChanged(event);" />
</mx:VBox>
</mx:WindowedApplication>
It probably isn't written in a way (just attributed) you expected but it does what you need.
If this isn't a solution you could think of extending the Label in the following manner.
Crete custom Label:
radekg/MyLabel.as
package radekg {
import mx.controls.Label;
public class MyLabel extends Label {
public function MyLabel() {
super();
}
private var _myText:String;
override public function get text():String {
return _myText;
}
override public function set text(value:String):void {
if ( value != null && initialized ) {
// store the copy so the getter returns original text
_myText = value;
// shorten:
var shortened:Boolean = false;
while ( measureText(value).width > (width-30) ) {
value = value.substr(0,value.length-1);
shortened = true;
}
super.text = value + ((shortened) ? "..." : "");
}
}
}
}
And use it like that:
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:controls="radekg.*">
<mx:VBox width="50%">
<controls:MyLabel width="100%" id="lbl" text="{ti.text}" />
<mx:TextInput width="100%" id="ti" />
<mx:Button label="Display label text" click="mx.controls.Alert.show(lbl.text)" />
</mx:VBox>
</mx:WindowedApplication>
This can be used simply with binding. Once you type very long text into the text input and the Label displays ... click on the button. You'll notice that text() getter returns original text.
Hope this helps.