In my code, I have a list made up of items from a dataprovider. The itemRenderer for the list consists of a BorderContainer with text in it. I'm mimicking a row of buttons on a scrolling list. I'd like the cursor to change to the hand cursor as it passes over the "button", but the pointer only changes in the part of the BorderContainer that isn't covered by the text.
I've set the buttonMode to true for the list, the BorderContainer, and the text, so why isn't the cursor changing when passing over the text?
This is the list code
<s:List id="listProject" width="100%" height="100%" horizontalScrollPolicy="off" allowMultipleSelection="false"
click="listProject_clickHandler(event)" itemRenderer="ProjectRenderer"
dataProvider="{listProjects}" creationComplete="listProject_creationCompleteHandler(event)" buttonMode="true">
And this is the renderer
<?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">
<!--<s:Label text="{data.header}"/>-->
<s:states>
<s:State name="normal"/>
<s:State name="hovered"/>
<s:State name="selected"/>
</s:states>
<!--<s:Image source.normal="{data.image1}" source.hovered="{data.image2}"/>-->
<s:BorderContainer width="200" height="50" backgroundColor="{data.color}"
borderColor.selected="#FFFFFF" borderVisible.normal="false"
borderVisible.selected="true" borderWeight.selected="4" borderStyle.selected="inset" buttonMode="true">
<s:layout>
<s:HorizontalLayout verticalAlign="middle" horizontalAlign="center"/>
</s:layout>
<mx:Text width="200" text="{data.header}" selectable="false"
color="#FFFFFF" fontSize="15" fontWeight="bold" leading="0" textAlign="center" buttonMode="true" useHandCursor="true"/>
</s:BorderContainer>
</s:ItemRenderer>
Just use a Spark Label instead of mx:Text and you'll be fine.
Also, there's some redundancy in your code. I took the liberty of trimming it down a little:
<s:BorderContainer width="200" height="50" backgroundColor="{data.color}"
borderColor.selected="#FFFFFF" borderVisible.normal="false"
borderVisible.selected="true" borderWeight.selected="4"
borderStyle.selected="inset" buttonMode="true">
<s:Label text="{data.label}" color="#FFFFFF" fontSize="15" fontWeight="bold"
horizontalCenter="0" verticalCenter="0"/>
</s:BorderContainer>
This will also fix that nasty jumpiness of the text when hovered or selected.
As soon as you do not need interaction with mx:Text, you can set mouseChildren property of its parent to false. It will fix the problem. Here is a short example:
<s:ItemRenderer>
<s:BorderContainer width="200" buttonMode="true" useHandCursor="true" mouseChildren="false">
<mx:Text text="No many text" selectable="false" />
</s:BorderContainer>
</s:ItemRenderer>
Related
The checkboxes are updated correctly when I select one or more datagrid rows but when I select a checkbox for the first time the checkbox does not refresh until the pointer moves out of the datagrid row. How can I fix this?
<?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">
<s:DataGrid id="dg" x="344" y="48" selectionMode="multipleRows" requestedRowCount="4">
<s:columns>
<s:ArrayList>
<s:GridColumn>
<s:itemRenderer>
<fx:Component>
<s:GridItemRenderer>
<fx:Script>
<![CDATA[
import mx.controls.Alert;
import spark.components.DataGrid;
override public function prepare(hasBeenRecycled:Boolean):void
{
cb.selected = grid.selectionContainsIndex(rowIndex);
}
]]>
</fx:Script>
<s:CheckBox id="cb" label="" horizontalCenter="0"/>
</s:GridItemRenderer>
</fx:Component>
</s:itemRenderer>
</s:GridColumn>
<s:GridColumn dataField="dataField1" headerText="Column 1"></s:GridColumn>
<s:GridColumn dataField="dataField2" headerText="Column 2"></s:GridColumn>
<s:GridColumn dataField="dataField3" headerText="Column 3"></s:GridColumn>
</s:ArrayList>
</s:columns>
<s:typicalItem>
<fx:Object dataField1="Sample Data" dataField2="Sample Data" dataField3="Sample Data"></fx:Object>
</s:typicalItem>
<s:ArrayList>
<fx:Object dataField1="data1" dataField2="data1" dataField3="data1"></fx:Object>
<fx:Object dataField1="data2" dataField2="data2" dataField3="data2"></fx:Object>
<fx:Object dataField1="data3" dataField2="data3" dataField3="data3"></fx:Object>
<fx:Object dataField1="data4" dataField2="data4" dataField3="data4"></fx:Object>
</s:ArrayList>
</s:DataGrid>
</s:Application>
Change this:
<s:CheckBox id="cb" label="" horizontalCenter="0"/>
To:
<s:CheckBox id="cb" label="" horizontalCenter="0" enabled="false"/>
I just recommend you to use enabled property.
I think the dispatched "click event" from both checkbox and gridColumn, then returned function prevented each other.
If enabled property set false, your click event dispathed on only gridColumn then using cb.selected=grid.selectionContainsIndex(rowIndex); correctly occupy if you want to show checkbox enabled, you can use CSS or skinclass
The easiest way is to just use the renders selected state as suggested by RIAStar. However if you are using global skinning doing the custom drawing does not work, use either a skinnable container or as I have just put the checkbox in there but dont make it respond to mouse commands. For the multi selection, as long as your grid is setup to multiple rows or columns you can simply capture the mouse events and force the ctrl key which makes them handle multi select.
<s:GridItemRenderer
mouseDown="mouseDownHandler(event)"
mouseUp="mouseUpHandler(event)"
buttonMode="true"
mouseChildren="false"
useHandCursor="true"
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark"
>
<s:states>
<s:State name="normal"/>
<s:State name="selected"/>
</s:states>
<fx:Script>
<![CDATA[
protected function mouseUpHandler(event:MouseEvent):void {
event.ctrlKey = true;
}
protected function mouseDownHandler(event:MouseEvent):void {
event.ctrlKey = true;
}
]]>
</fx:Script>
<s:CheckBox
id="check"
selected.normal="false"
selected.selected="true"
horizontalCenter="0"
verticalCenter="0"
/>
</s:GridItemRenderer>
I ended up simply doing this:
<s:GridColumn dataField="myBoolean" headerText="Returned" width="55">
<s:itemRenderer>
<fx:Component>
<s:GridItemRenderer>
<s:CheckBox id="cb1" selected="{data.myBoolean}" change="{data.myBoolean=cb1.selected}"/>
</s:GridItemRenderer>
</fx:Component>
</s:itemRenderer>
</s:GridColumn>
You can just fake the CheckBox by drawing a CheckBox shape in the ItemRenderer and use the states to show the tick.
<s:GridItemRenderer>
<s:states>
<s:State name="normal" />
<s:State name="hovered" />
<s:State name="selected" />
</s:states>
<!-- checkbox graphics -->
<s:Group width="16" height="16" horizontalCenter="0" verticalCenter="0">
<s:Rect left="0" right="0" top="0" bottom="0">
<s:fill>
<s:SolidColor color="0xffffff" />
</s:fill>
<s:stroke>
<s:SolidColorStroke color="0xa9aeb2" />
</s:stroke>
</s:Rect>
<!-- tick, only shown when selected -->
<s:Rect includeIn="selected" width="8" height="8" horizontalCenter="0" verticalCenter="0">
<s:fill>
<s:SolidColor color="0x90b40c" />
</s:fill>
</s:Rect>
</s:Group>
</s:GridItemRenderer>
This is a simplified graphic for a checkbox, but you can go grab the code from the spark CheckBoxSkin and copy/paste it in the itemrenderer. Just might have to change some state names.
This will not deselect a single row though when you hit the CheckBox of an already selected row, unless you hold the CTRL key down. That's the default behavior of the DataGrid component. I'm afraid you'll have to create your own subclass of DataGrid if you want to prevent that behavior.
Another important thing to know: setting the selected property on the itemrenderers doesn't change the selectIndices of the DataGrid. Hence on the next commitProperties() cycle the value you set in the renderer will be overridden by the DataGrid.
Old answer: (before edit)
The ItemRenderer class (and thus the GridItemRenderer class too) has a selected property.
So you could bind the checkboxes selected property to the itemrenders, like so:
<s:CheckBox selected="{selected}" horizontalCenter="0" />
You'd have to create a separate ItemRenderer class for that to work though instead of an inline one.
If you absolutely want to go the inline way you can always override the selected setter.
<s:GridItemRenderer>
<fx:Script>
<![CDATA[
override public function set selected(value:Boolean):void {
super.selected = cb.selected = value;
}
]]>
</fx:Script>
<s:CheckBox id="cb" horizontalCenter="0"/>
</s:GridItemRenderer>
I have a small problem with rollover effects. First time after loading everything's fine. But after clicking the button twice (= going to studyState and then coming back to Sate1) the rollover effect on the bordercontainer stops working.
I don't have a clue what the reason could be. Please give me a hint.
<?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:Declarations>
<s:AnimateColor id="animateColorON" colorPropertyName="backgroundColor" colorFrom="#FFFFFF" colorTo="#e7eae4" duration="300"/>
<s:AnimateColor id="animateColorOFF" colorPropertyName="backgroundColor" colorFrom="#e7eae4" colorTo="#FFFFFF" duration="600"/>
</fx:Declarations>
<s:transitions>
<s:Transition id="t1" autoReverse="true">
<s:CrossFade
target="{this}"
duration="1500" />
</s:Transition>
</s:transitions>
<s:states>
<s:State name="State1" />
<s:State name="studyState" />
</s:states>
<s:VGroup id="globalGroup" includeIn="State1" width="100%">
<s:Button label="State1 to studyState" click="this.currentState = 'studyState'" />
<s:BorderContainer width="100%" height="30" cornerRadius="4" borderVisible="false" buttonMode="true" rollOverEffect="animateColorON" rollOutEffect="animateColorOFF">
<s:HGroup width="100%" height="30" verticalAlign="middle" paddingLeft="5" paddingRight="5">
<s:Label id="p_dob_label" text="text" width="55%"/>
<s:Label id="p_dob_value" text="text" width="40%" verticalAlign="top" textAlign="right" color="#8DA576"/>
</s:HGroup>
</s:BorderContainer>
</s:VGroup>
<s:VGroup id="studyGroup" includeIn="studyState" width="100%">
<s:Button label="studyState to State1" click="this.currentState = 'State1'" />
</s:VGroup>
</s:Application>
Here is a fix. add an event listener for when the state changes. I use the currentStateChangeevent:
<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" currentStateChange="application1_currentStateChangeHandler(event)">
In the listener, manually set the rollOverEffect and rollOutEffect effects:
<fx:Script>
<![CDATA[
import mx.events.StateChangeEvent;
protected function application1_currentStateChangeHandler(event:StateChangeEvent):void
{
// TODO Auto-generated method stub
if(bc){
bc.setStyle('rollOverEffect',animateColorON);
bc.setStyle('rollOutEffect',animateColorOFF);
}
}
]]>
</fx:Script>
Be sure to give the BorderContainer an ID. I used bc:
<s:BorderContainer id="bc" width="100%" height="30" cornerRadius="4" borderVisible="false" buttonMode="true" rollOverEffect="animateColorON" rollOutEffect="animateColorOFF" >
I'm not sure why those effects are lost. My best theory is that this has something to do with how the ActionScript is generated behind the scenes. Even though the rollOverEffect and rollOutEffect appear to be properties on the component, they are actually implemented behind the scenes as styles. I bet, for some reason, when switching states the 'effect' styles are not reset. You'd have to look at the generated ActionScript to know for sure, though.
I am using a Skin for my buttons in Flex 4. The skinned buttons are displayed correctly.
Now I want the label of the buttons to respond as such:
Font Weight should be Bold when "RollOver" or "MouseOver" over the buttons.
I tried doing it in the mxml below by including the rollOver property to the Label on this button, but it doesnt seem to work.
Am I doing it at the right place, that is in the Skin mxml file? Or should I do it for each button in the Application itself?
Skin Code:
<?xml version="1.0" encoding="utf-8"?>
<s:SparkSkin xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<fx:Metadata>
<![CDATA[
[HostComponent("spark.components.Button")]
]]>
</fx:Metadata>
<fx:Script>
<![CDATA[
protected function labelDisplay_rollOverHandler(event:MouseEvent):void
{
FontWeight.BOLD;
FontStyle.ITALIC;
}
]]>
</fx:Script>
<s:states>
<s:State name="up" />
<s:State name="over" />
<s:State name="down" />
<s:State name="disabled" />
</s:states>
<s:BitmapImage includeIn="up" source="{getStyle('upSkin')}" width="100%" height="100%"/>
<s:BitmapImage includeIn="over" source="{getStyle('overSkin')}" width="100%" height="100%"/>
<s:BitmapImage includeIn="down" source="{getStyle('downSkin')}" width="100%" height="100%"/>
<s:BitmapImage includeIn="disabled" source="{getStyle('disabledSkin')}" width="100%" height="100%"/>
<s:Label id="labelDisplay" textAlign="right" maxDisplayedLines="1" horizontalCenter="1"
verticalAlign="middle" verticalCenter="1" left="10" right="10" rollOver="labelDisplay_rollOverHandler(event)" />
</s:SparkSkin>
You can do this easily by changing the fontWeight style for the "over" state. You can do this in MXML by specifying the style followed by the state you want that style to be applied to, like so:
<s:Label id="labelDisplay" fontWeight.over="bold" textAlign="right" maxDisplayedLines="1" horizontalCenter="1"
verticalAlign="middle" verticalCenter="1" left="10" right="10" />
Hope that helps.
I would like to write a transition where all the elements from State1 rotate around Y axis and then show elements from State2
This s illustrated in the dummy code below (just imagine Label 1 is a Group).
<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">
<s:states>
<s:State name="State1"/>
<s:State name="State2"/>
</s:states>
<s:transitions>
<s:Transition fromState="*" toState="State2" autoReverse="true">
<s:Rotate3D target="{label2}" angleYFrom="-90" angleYTo="0" autoCenterTransform="true" duration="1000"/>
</s:Transition>
</s:transitions>
<fx:Declarations>
<s:Rotate3D id="phaseOut" target="{label1}" angleYFrom="0" angleYTo="90" autoCenterTransform="true" duration="1000" effectEnd="currentState='State2'" />
</fx:Declarations>
<s:Label id="label1" includeIn="State1" text="This is state 1" horizontalCenter="0" verticalCenter="0"/>
<s:Label id="label2" includeIn="State2" text="This is state 2" horizontalCenter="0" verticalCenter="0"/>
<s:Button label="Change" horizontalCenter="0" verticalCenter="30" click.State1="phaseOut.play()" click.State2="currentState='State1'"/>
</s:WindowedApplication>
My first problem is when the state transition is invoked, all elements from State1 are already gone, hence I have to split the transition in two hacks (see "phaseOut")
This seems really poor since I am essentially rewritting the transition mechanism.
Q1: Is there a "clean" way to transition elements that do not belong to State2 ?
The second problem is when you revert back to State 1, elements have been rotated.
Q2: Is there such a thing as "autoReverse" for animations?
Thanks for your time!
Instead of doing two transitions, you can add the 'remove' filter to isolate an effect just on items being removed and use the RemoveChildAction to let the transition know when to execute the action to remove the items.
Info on RemoveChildAction:
http://livedocs.adobe.com/flex/3/langref/mx/effects/RemoveChildAction.html
Info on filters:
http://livedocs.adobe.com/flex/3/langref/mx/effects/Effect.html#filter
Effects have a reverse method to play it in reverse:
http://livedocs.adobe.com/flex/3/langref/mx/effects/Effect.html#reverse%28%29
Although I've heard mixed results from people about how successful it is.
Using viewstacks seems to do the trick nicely:
<?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" >
<fx:Declarations>
<s:Rotate3D id="phaseOut" angleYFrom="0" angleYTo="90" duration="500" autoCenterTransform="true" />
<s:Rotate3D id="phaseIn" angleYFrom="-90" angleYTo="0" duration="500" autoCenterTransform="true" startDelay="500" />
</fx:Declarations>
<mx:ViewStack id="viewstack1" width="200" height="200" horizontalCenter="0" verticalCenter="0">
<s:NavigatorContent label="View 1" width="100%" height="100%" hideEffect="{phaseOut}" showEffect="{phaseIn}" >
<s:Label id="label1" text="This is state 1" horizontalCenter="0" verticalCenter="0"/>
</s:NavigatorContent>
<s:NavigatorContent label="View 2" width="100%" height="100%" hideEffect="{phaseOut}" showEffect="{phaseIn}">
<s:Label id="label2" text="This is state 2" horizontalCenter="0" verticalCenter="0"/>
</s:NavigatorContent>
</mx:ViewStack>
<s:Button label="Toggle" click="viewstack1.selectedIndex=(viewstack1.selectedIndex==0?1:0)" horizontalCenter="0" top="10"/>
</s:Application>
The code is neat and the effect is exactely as expected
i have an HGroup that contains a DataGroup with an ArrayCollection for a dataProvider.
the DataGroup has a VScrollBar attached to it.
how can i make sure that whenever new rows are added to the ArrayCollection, the dataGroup will scroll down ?
this window is gonna be used for a chat application so whenever new rows are added i need to see the new rows.
i know that i can perform the following command to scroll down: chatScrollBar.value=chatScrollbar.maximum
but what event do i need to attach to in order to run this command whenever a new row is visible ?
<s:HGroup width="100%">
<s:DataGroup id="vertView"
clipAndEnableScrolling="true" width="100%" height="60"
dataProvider="{chatMessages}">
<s:itemRenderer>
<fx:Component>
<s:ItemRenderer width="100%" height="8">
<s:states>
<s:State name="normal" />
<s:State name="hovered" />
</s:states>
<s:RichText text="{ data }" textAlign="left" paddingLeft="2" color="black" color.hovered="blue"/>
</s:ItemRenderer>
</fx:Component>
</s:itemRenderer>
<s:layout>
<s:VerticalLayout useVirtualLayout="true"/>
</s:layout>
</s:DataGroup>
<s:VScrollBar id="chatScrollBar" viewport="{vertView}"
height="{vertView.height}" />
</s:HGroup>
You have to set the verticalScrollPosition. See here or here. Or try this for a Spark List:
<s:List id="list"
horizontalCenter="0"
verticalCenter="0">
<s:layout>
<s:VerticalLayout id="vLayout" requestedRowCount="4"
verticalScrollPosition="{vLayout.maxScrollPosition}" />
</s:layout>
</s:list>
Try using Scroller, I think it is as simple as:
<s:Scroller >
<s:HGroup>
...
</s:HGroup>
</s:Scroller>
You shouldn't need to hook up your own scrollbar.