In flex3, List has isItemSelected() method, but I didn't find them in flex4. My scenario is determining whether the current ItemRenderer is selected or not, and then depends on the selected value, do some logic on specific component in the ItemRenderer(suppose ItemRenderer has an Image component and Label component, I only want to do some logic on the Image)
In Flex 4, item renderer functionality can make better use of states. What this means is that they have the default states, which we can use them to implement state-specific logic:
normal
hovered
selected
up
If you want to do something when the item becomes selected, you could add a listener for the stateChangedComplete event, and implement your logic in that handler (of course, you would have to test if the current statate is 'selected'). The code could look something like this:
<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"
stateChangeComplete="stateChangedHandler()"
autoDrawBackground="false">
<fx:Script>
<![CDATA[
protected function stateChangedHandler():void
{
if(currentState == "selected")
{
// implement your logic here
}
}
]]>
</fx:Script>
<!-- Your original MXML code here -->
</s:ItemRenderer>
This would be the way to go, if you need some non-trivial logic done. If, however, you just need to change some attributes on the image, when the item renderer becomes selected, you could just specify a state-specific property/value pair on the element instead, like so (let's assume the images are faded by default, and when the item is selected, you want to fade them in, for the sake of the explanation):
<s:Image alpha="0.5" alpha.selected="1" />
This way, no listener/handler is required.
Hope this helps. Have a great day.
Related
I have a spark List and using ArrayCollection as dataprovider for list. Initially when the list is populated sorting is done , but i need to add items to list quite frequently as they update on server. After i add item i have to re-sort the list. After applying sort and calling refresh() scrollbar resets itself to top.
I am looking for a way so that i can add items to list and keep sorting the list without resetting the scrollbar to top position.
I have read similar question earlier but in that question most users have said to store the VerticalScrollPostion in a variable and apply that VerticalScrollPosition back after the sorting is done. The problem with this is there is a jerk in scrollbar , as it goes to top after refresh and then scrolls back to position as applied. So this solution is not possible as items are added very frequently as it will make the scrollbar going up and down very frequently and annoying the users. Also does't look nice n professional
I need a way so that scrollbar stays in it place even after sorting is done.
Also i want to keep using useVirtualLayout=true for the List.
I was also trying to extent ArrayCollection but could't find a way to solve my problem.
Below is simple example(extracted from my main application) to illustrate the problem. Just Scroll to bottom of list and then press the Sort button(which will sort the list) and the scrollbar will jump to top position. I want scrollbar to stay at its position even after pressing Sort button:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600" creationComplete="application1_creationCompleteHandler(event)">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.collections.SortField;
import spark.collections.Sort;
[Bindable] protected var ar_c:ArrayCollection;
protected var dgArray:Array=new Array();
protected function application1_creationCompleteHandler(event:FlexEvent):void
{
for(var i:int=0;i<20;i++)
dgArray.splice(-1,0,{label:"h"+i});
ar_c= new ArrayCollection(dgArray);
}
protected function sort_list():void{
var sort:spark.collections.Sort=new Sort();
sort.fields=[new SortField("label",false,true)];
ar_c.sort=sort;
ar_c.refresh();
}
]]>
</fx:Script>
<s:List x="42" y="41" id="mylist" width="199" height="253" dataProvider="{ar_c}" useVirtualLayout="true"></s:List>
<s:Button x="295" y="40" label="Sort" click="sort_list();"/>
</s:Application>
I think a sort is applied once and then everything added to the ArrayCollection later on is sorted on. You only have to add the sort once, and then call refresh once to apply the sort.
I am confused on how to get this.. so i have a user list when someone joins it adds them to a List (their username) i have a way to find out if they are admin or not i just need to know how i can change the color of each user in that list... this is an example....
If list supported html this would work fine
onlineUsers.addItem({label:"<font color='$ffffff'>users[i].userName+"_GUEST</font>",id:users[i].userID,guest:"True"});
userList.dataProvider = onlineUsers
But list do not support html, anyone know a work around with this?
Generically, your answer is to use an itemRenderer.
All a list class does is display other components (renderers) and send those components data from the dataProvider to display. Really, you mean the default itemRenderer doesn't support HTML. Technically you could make an itemRenderer that would support HTML to give you the color change you need; but I'd take a different.
Add a property to the user object that specifeis whether they are an admin user or not. Then iF the user is an admin user; then; change the color. Conceptually like this:
<s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark"
autoDrawBackground="false" dataChange="onDataChange()">
<fx:Script>
<![CDATA[
public function onDataChange():void{
labelDisplay.text = data.userName + "_Guest";
if(data.isAdmin){
labelDisplay.setStyle('color',0xff0000);
} else {
labelDisplay.setStyle('color',0x00FF00);
}
}
]]>
</fx:Script>
<s:Label id="labelDisplay"
/>
</s:ItemRenderer>
I am trying to process the user feeds retrieved from facebook by dynamically adding them to the flash app, and i need the feed to be displayed in a box with its publisher name and picture beside it (as normal)..
the question is how could i display them aligned under each other if i don't know their heights? is there a way to just add them under each other without specifying the height?
like in html adding divs under each other?
and other question how to add a scroll bar if the content exceeds the flash window?
iam new to as3 & flash so any advice will help.. thanks..
my code:
protected function getFeedsHandler(result:Object,fail:Object):void
{
if(result)
{
var i:Number;
for(i=0;i<10;i++)
if(result[i])
{
var fdLbl= new Label();
addElementAt(fdLbl,1);
fdLbl.text=result[i].message;
fdLbl.x=20;
fdLbl.y=(i+2)*100;
fdLbl.width=400;
var fdImg= new Image();
addElementAt(fdImg,1);
fdImg.source=FacebookDesktop.getImageUrl(result[i].from.id,"small");
fdImg.x=20;
fdImg.y=(i+2)*80;
fdImg.width=400;
var nameLbl= new Label();
addElementAt(nameLbl,1);
nameLbl.text=result[i].from.name;
nameLbl.x=20;
nameLbl.y=(i+2)*90;
}
}
the interface:
<s:Button id="loginoutBtn" right="10" top="10" label="Log out"
click.loggedin="logout(event)"
label.loggedout="Log in" click.loggedout="login(event)"/>
<s:Form includeIn="loggedin" left="70" top="10">
<s:FormItem label="User">
<s:Label id="nameLbl" text=""/>
</s:FormItem>
<s:FormItem label="birthday">
<s:Label id="brthday"/>
</s:FormItem>
<s:FormItem label="feeds">
<s:Image id="feedImg"/>
<s:Label id="feedLbl" x="0"/>
<s:Label id="statusLbl" width="405"/>
</s:FormItem>
</s:Form>
<s:Image id="userImg" includeIn="loggedin" left="10" top="10" width="50"/>
So it looks like you're using Flex and specifically you're using the spark components. In using Spark there's a couple of options on how you would achieve this, one option is to put the component you made above into a VGroup. The VGroup vertically stacks visual components, it has a property called gap you can set to add or remove spacing between the components (number of pixels between each object nested into the VGroup). The addition of a s:Scroller component wrapping the VGroup will give you the scroll bars, essentially the way this works is you set the size on the s:Scroller to the size you want to visible (can be a percentage like 100% width/height of the view/container), then you set no explicit size on the VGroup, the VGroup will expand to have a height equal to the height of all the components nested in it plus any padding plus the gap space, the scroller will take care of figuring out how big the scroll tab button should be and adjust the scrollRect on the VGroup for you, essentially it's going to work like magic :). This would look something like the following:
<s:Scroller width="100%" height="100%">
<s:VGroup id="loadedContentVGroup">
<yourpackage:YourCustomComponent/>
</s:VGroup>
</s:Scroller>
If you don't constrain the scroller's height and width it will not work as it doesn't have a defined region to draw in and will just become as large as it's children (the nested components). In all likelihood you would add elements to the VGroup in AS3 so you'd probably want to give it an ID.
Your other option is to use a spark List and then set your visual component as the itemRenderer. For each data element in the data provider for the list (some sort of collection class, ArrayCollection, XMLCollection etc.) it will set an element of the array as the "data" property on your visual component. You would then use bindings to the data property in your visual component to make sure if the renderer is re-used/recycled the elements within it update automatically. Here's an example of that:
<s:List dataProvider="{new ArrayCollection([{name:'Shaun', birthday:'04-28-1983', imageSource:'someImage.png'}])}"
itemRenderer="views.YourCustomComponent"
width="100%"/>
In your case the dataProvider for the list could be assigned in the result handler code you have above, it appears result is an array so you can just wrap it in an ArrayCollection like myList.dataProvider = new ArrayCollection(result);
Here's a potential view definition for a single entry in the list (the itemRenderer class used above)
[views.YourCustomComponent]
<?xml version="1.0" encoding="utf-8"?>
<s:VGroup xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
implements="mx.core.IDataRenderer">
<fx:Script>
<![CDATA[
private var _data:Object;
[Bindable(eventName="dataChanged")]
public function get data():Object
{
return _data;
}
public function set data(value:Object):void
{
if(_data == value)
return;
_data = value;
dispatchEvent(new Event("dataChanged"));
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:Label text="{data.name}"/>
<s:Label text="{data.birthday}"/>
<s:Image source="{data.imageSource}"/>
</s:VGroup>
There are certainly caveats and alternatives to these answers but this will hopefully get you going in the right direction, if you're doing this for mobile development be sure to check out the recommendations with regard to creating renderers for mobile, the idea here is to reduce the processing needed for creating and re-using renderers on mobile as much as possible to lighten the load on the CPU and memory.
One other thing to note it appears your mixing defining the view in AS3 and MXML, while this is technically okay I find it to be a bit confusing myself. I would normally define the entire layout in MXML, or if need be entirely in AS3. If defined in MXML and a component needs to be conditionally hidden by code I would just toggle the visible/includeInLayout properties. I suppose this is just a preference but it seems it could get hairy determining what is causing particular layouts to occur when you have to look between the two to piece it together.
References to the AS3 reference for classes used:
http://help.adobe.com/en_US/FlashPlatform/beta/reference/actionscript/3/spark/components/VGroup.html
http://help.adobe.com/en_US/FlashPlatform/beta/reference/actionscript/3/spark/components/Scroller.html
http://help.adobe.com/en_US/FlashPlatform/beta/reference/actionscript/3/spark/components/List.html
Details on custom item renderers http://help.adobe.com/en_US/flex/using/WS03d33b8076db57b9-23c04461124bbeca597-8000.html <-- references Jeffry Houser a regular on SO with the name www.flextras.com
I'm trying to do this using only mxml, no <script> tags, although I don't necessarily need a solution that's only mxml. It was more of an educational exercise to see if I could do it all in mxml.
I have a custom component that has a slider and textinput and their value/text properties are bound together. I'm surfacing a few properties of the slider in my component so that it can sort of be treated like a slider.
<?xml version="1.0" encoding="utf-8"?>
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" verticalAlign="middle" horizontalGap="0">
<mx:int id="value">{slider.value}</mx:int>
<mx:int id="minimum">0</mx:int>
<mx:int id="maximum">100</mx:int>
<mx:int id="tickInterval">25</mx:int>
<mx:Array id="labels">['0%','50%','100%']</mx:Array>
<mx:HSlider id="slider" liveDragging="true" snapInterval="1"
value="{int(input.text)}"
minimum="{minimum}"
maximum="{maximum}"
tickInterval="{tickInterval}"
labels="{labels}"/>
<mx:Spacer width="25"/>
<mx:TextInput id="input" restrict="0-9" text="{slider.value}" maxChars="3" width="30"/>
<mx:Label text="%"/>
</mx:HBox>
Notice the slider's VALUE property is bound to the input field's TEXT property, and vice versa. A two-way binding. This lets the user slide the thumb or type in the input field to select a value and they stay in sync with each other.
Also, the component's VALUE property is bound to the slider's VALUE property so that the value of this component will always contain the value of the slider (so that the component can be used like a slider).
The slider's properties are also bound to the component's properties (min, max, tick marks)
The problem is that I want to initialize the value of the slider from the value of the component, but the slider's value is already bound to the textinput. Can I also bind it to the component?
My application will have something like this:
<local:mycomponent minimum="20" maximum="80" labels="['20','50','80']" value="40"/>
A few things I tried that didn't work:
(1) I had an initialize handler.
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" initialize="slider.value=value">
This worked if my app had
myslider.value = 40;
but didn't work if I had
<local:mycomponent value="40"/>
(2) I tried a creationComplete handler
(3) I tried mx:binding
<mx:Binding source="slider.value" destination="this.value"/>
It seems like I'm missing something simple.
Note the curly brackets:
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" initialize="{slider.value=value}">
In the brackets, there may be any code. Without them, there can only be event handler (function).
Whenever I use a lot of nesting inside mxml components(including many states) with quite a few Vboxs and Other containers, I always get confused when I see a scrollbar appearing on screen, especially with datagrid inside it(I always want to show scroll bar in datagrid and not on the parent container, for which I usually set the height and width of datagrid smaller than its parent container at run time).
My question is, how could I possibly know (QUICKLY), using debugger, that which component is the source of scroll bar that I see on screen (if there are more than one, then some propoerty of compnent must change when I scroll it up or down).
Thanks.
I realise that this answer isn't using the debugger directly. I mean it as an idea for a simple tool really.
I had a quick go at putting together a simple app that's function is to report what display object is dispatching a mouse wheel event. It does not matter to the app if there is a scrollbar or not, but I guess you could adjust it to your needs. Its a quick start really, here's the code...
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" minWidth="955" minHeight="600"
creationComplete="init()">
<mx:HBox id="HBoxWithScrollbar" width="600" height="500">
<mx:HBox width="800" height="800">
</mx:HBox>
</mx:HBox>
<mx:TextArea id="record" height="300" width="600"/>
<mx:Script>
<![CDATA[
private function init():void{
record.text = 'Scroll Record\n';
this.addEventListener(MouseEvent.MOUSE_WHEEL, recordObject);
for each (var obj:DisplayObject in this.getChildren()){
obj.addEventListener(MouseEvent.MOUSE_WHEEL, recordObject);
}
}
protected function recordObject(event:MouseEvent):void{
record.text += (event.target as DisplayObject).toString() + '\n';
}
]]>
</mx:Script>
</mx:Application>
The important thing here really is to see that you can pick up the mouse wheel event at the top level, because it bubbles by default, and isn't cancelable.
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/InteractiveObject.html#event:mouseWheel
Once you've got hold of that event, you've got options.
This was built using version 3.6 of the Flex SDK, but it wouldn't take much to build a 4.x version. I am simply displaying "toString()" value of the target display object, but that could be any attribute you want. You'll probably want to put in some error handling for the loop adding events, and also in the event handler. As I said, its just a start, and I hope it helps.