Flex 4: mx|tree - how can i disable items selection? - apache-flex

I want to create a tree that it's child nodes contain specific flex components that I created. I override the default ItemRenderer to achieve this.
In my ItemRenderer i have:
two states: item and root_item.
a function that executes after creation complete that using the data validates the proper state that this component should be in and changes the currentState to the desired state.
My problem is that once the user clicks on any of the elements, it changes automatically to the first state which mess things up.
how can I disable items selection at all ? of course I want the user to be able to drill up and down the trees but not to select the items.
thanks!
update
The Item changes states on hover effect, so or I disable items selection or somehow i prevent the hover effect from changing the state.
another update
this is my code:
the main TreeTest.xml:
<?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" autoLayout="false" minHeight="600">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
<fx:XML id="treeData">
<node label="notifications">
<node label="Winnings" is_root="yes">
<node label="winner"/>
</node>
<node label="Challenges" is_root="yes">
</node>
<node label="Achievements" is_root="yes">
</node>
<node label="Lucky charms" is_root="yes">
</node>
<node label="Friend requests" is_root="yes">
</node>
</node>
</fx:XML>
</fx:Declarations>
<fx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.controls.listClasses.ListBase;
import mx.events.ListEvent;
protected function tree_itemClickHandler(event:ListEvent):void
{
tree.selectedItem = null;
event.preventDefault();
}
protected function tree_itemRollOverHandler(event:ListEvent):void {
event.preventDefault();
}
]]>
</fx:Script>
<mx:Tree id="tree" itemRollOver="tree_itemRollOverHandler(event)" itemClick="tree_itemClickHandler(event)" dataProvider="{treeData}" folderOpenIcon="{null}" folderClosedIcon="{null}" defaultLeafIcon="{null}" width="1024" height="768" labelField="#label" itemRenderer="TreeItemRenderer" showRoot="false" allowMultipleSelection="false" allowDragSelection="false"/>
</s:Application>
as you can see here I tried to prevent both itemRollover and itemClick but it did not resolve my problem.
this is the TreeItemRenderer.xml:
<?xml version="1.0" encoding="utf-8"?>
<s:MXTreeItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" creationComplete="init()">
<s:states>
<s:State name="root_item" />
<s:State name="item" />
</s:states>
<fx:Script>
<![CDATA[
import com.flexer.Debug;
import mx.binding.utils.ChangeWatcher;
import mx.controls.Alert;
import mx.events.StateChangeEvent;
private function _stateChangeEventHandler(e:StateChangeEvent):void {
Alert.show("state changed to " + e.target.currentState);
}
private function init():void {
var theXML:XMLList = XMLList(this.data);
var theState:String =( theXML.attribute("is_root") == "yes" ? "root_item" : "item");
this.currentState=theState;
this.addEventListener(StateChangeEvent.CURRENT_STATE_CHANGE,this._stateChangeEventHandler);
// Alert.show(theXML.attribute("is_root"));
// Alert.show(theXML.attribute("label") + (theXML.attribute("is_root") == "yes" ? "true" : "false"));
}
]]>
</fx:Script>
<s:HGroup left="0" right="0" top="0" bottom="0" verticalAlign="middle" includeIn="root_item">
<s:Rect id="indentationSpacer" width="{treeListData.indent}" percentHeight="100" alpha="0">
<s:fill>
<s:SolidColor color="0xFFFFFF" />
</s:fill>
</s:Rect>
<s:Group id="disclosureGroup">
<s:BitmapImage source="{treeListData.disclosureIcon}" visible="{treeListData.hasChildren}" />
</s:Group>
<s:BitmapImage source="{treeListData.icon}" />
<s:Label id="labelField" text="{treeListData.label}" paddingTop="2"/>
</s:HGroup>
</s:MXTreeItemRenderer>

You can add an event handler to the tree for the itemClick event
itemClick="tree_itemClickHandler(event)"
Then in the event handler you can cancel the itemClick event
protected function tree_itemClickHandler(event:ListEvent):void
{
tree.selectedItem = null;
event.preventDefault();
}
Update:
The item renderer has 3 default states: normal, hovered, and selected. You need to use basedOn with the 3 default states to reference your custom states. Ideally you would want the state in your data so you can avoid all of the work you're doing in init(). It would also allow you to bind basedOn to the state of your data as such:
<s:states>
<s:State name="normal" basedOn="{data.#state}"/>
<s:State name="hovered" basedOn="{data.#state}"/>
<s:State name="selected" basedOn="{data.#state}"/>
<s:State name="root_item" />
<s:State name="item" />
</s:states>
Then regardless of the default state that is sent by the parent list the display will be based upon what is in your data.

Related

error when using custom class in itemrenderer

I created a subclass of Event Class. I used the subclass in an itemrenderer, no error observed. But once I declared the itemrenderer to the List in the Main application, errors appears in the itemrenderer claiming "Type was not found or was not a compile-time constant: CustomDeleteEvent" and "Incorrect number of arguments: Expected no more than 1"
Please give me some advice. Thanks in advance.
In subclass :
package widgets.GetMap
{
import flash.events.Event;
public class CustomDeleteEvent extends Event
{
public static const DELETE_ITEM:String = "DELETE_ITEM";
public var deletedItem:String;
public function CustomDeleteEvent(type:String, deletedItem:String)
{
super(type);
this.deletedItem = deletedItem;
}
}
}
In ItemRenderer :
<?xml version="1.0" encoding="utf-8"?>
<s:ItemRenderer name="CustomItemRen"
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"
minHeight="24">
<s:states>
<s:State name="normal" />
<s:State name="hovered" />
<s:State name="selected" />
</s:states>
<fx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.events.CloseEvent;
import spark.components.List;
protected function deleteHandler():void
{
var deleteItem:String = itemIndex.toString();
var tryevent:CustomDeleteEvent;
owner.dispatchEvent(tryevent,deleteItem);
Object(owner).dataProvider.removeItemAt(itemIndex);
}
]]>
</fx:Script>
<s:HGroup width="100%" height="100%"
verticalAlign="middle"
paddingLeft="2" paddingRight="2"
paddingTop="2" paddingBottom="2">
<s:Label id="lbl" text="{data.toString()}" width="100%" color="#30FF00"/>
<s:Button id="btn" includeIn="hovered,selected" y="-16" width="35" height="22" label="X"
accentColor="#FFFFFF" color="#FF0000" fontFamily="Verdana" fontSize="12"
fontWeight="bold" mouseDown="deleteHandler();" toolTip="Delete item"/>
</s:HGroup>
</s:ItemRenderer>
In the main application:
<s:Application name="Spark_List_itemRenderer_hovered_test"
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:Style source="test.css"/>
<s:List id="lst"
itemRenderer="CustomItemRen"
width="300"
horizontalCenter="0" verticalCenter="0">
<s:layout>
<s:VerticalLayout gap="0"
horizontalAlign="justify"
requestedRowCount="8" />
</s:layout>
<s:dataProvider>
<s:ArrayList>
<fx:Object label="Application" />
<fx:Object label="Label" />
<fx:Object label="List" />
</s:ArrayList>
</s:dataProvider>
</s:List>
</s:Application>
In this line, you didn't initialize the event object ("tryEvent" like new CustomDeleteEvent())..
and also in your "CustomDeleteEvent"..there is "type" parameter, which is not in "tryEvent" object...
var tryevent:CustomDeleteEvent;
owner.dispatchEvent(tryevent,deleteItem);
Use below code for dispatch the event....
owner.dispatchEvent(new CustomDeleteEvent(CustomDeleteEvent.DELETE_ITEM, deleteItem));
hope this will help you....

Flex 4 bug:inherited propery cause transition bug

i try to make some foldable groups, like an accordion component
i create a Base.as class and extends from Group,code like this
package
{
import flash.events.MouseEvent;
import spark.components.Group;
public class Base extends Group
{
public var header:Group;
public var body:Group;
public function Base()
{
super();
}
override protected function initializationComplete():void
{
super.initializationComplete();
if(header)
{
header.addEventListener(MouseEvent.CLICK,changeState);
}
}
protected function changeState(event:MouseEvent):void
{
if(currentState=="fold")
currentState="unfold";
else
currentState="fold";
}
}
}
and i defined two properties,header and body here,but i don't instantiate here,and then i worte a mxml component "FoldUnit.mxml" that extends "Base" and i will instantiate header and body in it,code like this
<?xml version="1.0" encoding="utf-8"?>
<local:Base xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" currentState="fold" clipAndEnableScrolling="true"
xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:local="*" width="200" >
<local:states>
<s:State name="fold" />
<s:State name="unfold" />
</local:states>
<local:transitions>
<s:Transition toState="fold">
<s:Sequence>
<s:Resize target="{this}" duration="400" />
<s:RemoveAction target="{body}" />
</s:Sequence>
</s:Transition>
<s:Transition toState="unfold">
<s:Resize target="{this}" duration="400" />
</s:Transition>
</local:transitions>
<s:Group id="header" width="100%">
<s:Rect height="40" width="100%">
<s:fill>
<s:SolidColor color="0xff0000" />
</s:fill>
</s:Rect>
</s:Group>
<s:Group id="body" includeIn="unfold" y="40" width="100%">
<s:Rect height="200" width="100%">
<s:fill>
<s:SolidColor color="0x00ff00" />
</s:fill>
</s:Rect>
</s:Group>
</local:Base>
after all,i placed three of FoldUnit in main application to simulate an accordion component.
<?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" xmlns:local="*">
<s:Group>
<s:layout>
<s:VerticalLayout variableRowHeight="true" />
</s:layout>
<local:FoldUnit />
<local:FoldUnit />
<local:FoldUnit />
</s:Group>
</s:Application>
everything like fine,however,if i try to click one red header to fold up,that green body just disappear immediately,looks like that RemoveAction tag is useless.
so,as i try to figure out why,i made things simple first,and i combine that function in Base.as into FoldUnit.mxml,then it works fine.
so,i wonder is there a bug or something?
PS:i don't wanna use a skinable component,coz' the size measure process always delayed,it's hard to location the scroll position in scroll bar.

Why is my height changes not taking part in my transition on state change?

I have a transition setup on my Spark component. One of the changes from state 1 to state 2 is the change in height. The value was getting applied immediately. Here is my code:
<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"
height="200" height.state2="400">
<s:states>
<s:State name="state1"/>
<s:State name="state2"/>
</s:states>
<s:transitions>
<s:Transition fromState="state1" toState="state2" >
<s:Sequence duration="2000" >
<s:Rotate3D target="{this}"
angleYFrom="0" angleYTo="90"
startDelay="0"
suspendBackgroundProcessing="true"
autoCenterTransform="true" />
<s:SetAction target="{this}" property="height"/>
<s:Rotate3D target="{this}"
angleYFrom="-90" angleYTo="0"
startDelay="0"
suspendBackgroundProcessing="true"
autoCenterTransform="true" />
</s:Sequence>
</s:Transition>
</s:transitions>
<s:Rect width="100%" height="100%" >
<s:fill>
<s:SolidColor color="#ff0000"/>
</s:fill>
</s:Rect>
</s:Group>
And then in my main application:
<?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" xmlns:local="*"
>
<local:MyGroup id="group" verticalCenter="0" horizontalCenter="0" width="400"/>
<s:Button label="Change states" click="group.currentState=group.currentState=='state1'?'state2':'state1';"/>
</s:Application>
The solution is to use the explicitHeight property rather than height. This is because height is a special property symbol that Flex uses.
From the SetProperty PSEUDONYMS property:
/**
* #private
* This is a table of pseudonyms.
* Whenever the property being overridden is found in this table,
* the pseudonym is saved/restored instead.
*/
private static const PSEUDONYMS:Object =
{
width: "explicitWidth",
height: "explicitHeight",
currentState: "currentStateDeferred"
};
So if you've set the height then it's actually explicitHeight that is set. Keep in mind another related property called RELATED_PROPERTIES. It contains additional data on percent width and height and explicit width and height information.
For more information see mx.states.SetProperty.
To solve the problem in this case we change this:
<s:SetAction target="{this}" property="height"/>
To this:
<s:SetAction target="{this}" property="explicitHeight"/>

Flex VerticalLayout HorizontalLayout element order based on state

in my mxml part I have
<s:layout.landscape>
<s:HorizontalLayout />
</s:layout.landscape>
<s:layout.portrait>
<s:VerticalLayout />
</s:layout.portrait>
after these tags i have couple of components, for example
<s:Button label="button 1"/>
<s:Button label="button 2"/>
what I would like to do is change the order of these components once a portrait or landscape state is entered. For example in portrait I have vertical layout button 1 followed by button 2
and in landscape I have horizontal layout where button2 is followed by button 1.
You can use this.swapElements(button1,button2);
Example: -
<?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" currentState="landscape">
<s:states>
<s:State name="landscape"/>
<s:State name="portrait"/>
</s:states>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import spark.events.IndexChangeEvent;
protected function dropdownlist1_changeHandler(event:IndexChangeEvent):void
{
// TODO Auto-generated method stub
if(event.target.selectedIndex == 0)
{
this.currentState = event.target.dataProvider[0];
this.swapElements(button1,button2);
}
else
{
this.currentState = event.target.dataProvider[1];
this.swapElements(button2,button1);
}
}
]]>
</fx:Script>
<s:layout.landscape>
<s:HorizontalLayout />
</s:layout.landscape>
<s:layout.portrait>
<s:VerticalLayout />
</s:layout.portrait>
<s:DropDownList selectedIndex="0" change="dropdownlist1_changeHandler(event)">
<s:ArrayCollection>
<fx:String>landscape</fx:String>
<fx:String>portrait</fx:String>
</s:ArrayCollection>
</s:DropDownList>
<s:Button id="button1" label="button 1"/>
<s:Button id="button2" label="button 2"/>
</s:Application>
Hope this may help...

keep 'down' state for spark skin button

I have spark skin a button with up, down, over, and disable states in a button component to create a modular. Is there a way when the user press and hold a key, the button will remain in 'down' state?
The button component include skinclass:
<?xml version="1.0" encoding="utf-8"?>
<s:Button xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
skinClass="wfm.keyBtn">
</s:Button>
Spark skin:
<?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>
[HostComponent("spark.components.Button")]
</fx:Metadata>
<s:states>
<s:State name="up"/>
<s:State name="over"/>
<s:State name="down"/>
<s:State name="disabled"/>
</s:states>
<s:Rect width="18" height="80" height.down="83" radiusX="1">
<s:stroke>
<s:SolidColorStroke color="0xaaaaaa" weight="1"/>
</s:stroke>
<s:filters>
<s:BevelFilter
distance="0"
angle="5" angle.down="5"
highlightColor="0xbbbbbb" highlightColor.down="0xaaaaaa"
highlightAlpha="1" highlightAlpha.down="1"
shadowColor="0xaaaaaa" shadowColor.down="0xffffff"
shadowAlpha="1" shadowAlpha.down="1"
blurX="5" blurX.down="6"
blurY="5" blurY.down="6"
strength="2" strength.down="2"
type="inner"
/>
</s:filters>
<s:fill>
<s:LinearGradient rotation="90">
<s:GradientEntry color="0xf3f3f3" color.down="0xfff963" />
</s:LinearGradient>
</s:fill>
</s:Rect>
</s:SparkSkin>
Create a subclass of Button and tell the skin to be in the down state whenever the key you care about is down. Also, whenever you detect the key is down (or no longer down), invalidate the skin state so the skin knows to check what state it should be in:
package mypackage
{
import spark.components.Button;
public class MyButton extends Button
{
private var _isKeyDown:Boolean = false;
private function get isKeyDown():Boolean {
return _isKeyDown;
}
private function set isKeyDown(b:Boolean):void {
_isKeyDown = b;
this.invalidateSkinState();
}
// Add handlers in here to set isKeyDown to true/false appropriately
override protected function getCurrentSkinState():String {
if (isKeyDown)
return "down";
else
return super.getCurrentSkinState();
}
}
}

Resources