How to create and manage a nested TabbedViewNavigator? - apache-flex

I'm trying to implement a nested TabbedViewNavigator (inside a View). I did this by following method 3 described in this post:
Flex - how to make a tabbed panel
Code here:
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" title="HomeView">
<s:TabbedViewNavigator width="100%" height="100%">
<s:ViewNavigator label="1st Tab" width="100%" height="100%"
firstView="views.FirstTabView"/>
<s:ViewNavigator label="2nd Tab" width="100%" height="100%"
firstView="views.SecondTabView"/>
<s:ViewNavigator label="3rd Tab" width="100%" height="100%"
firstView="views.ThirdTabView"/>
</s:TabbedViewNavigator>
However if I call "navigator.pushView(someView)" inside one of the child views - let's say FirstTabView - the new view will be pushed into the nested TabbedViewNavigator instead of the parent's view navigator. That is not what I want. I want to completely change the application state and show a new view. How can I achieve that? Should I listen (in the main view) for changes within the nested TabbedViewNavigator and then push the new view from there? Or should I just do this some other way?

I solved it by using a custom event (it's custom because I need to get data from it).
Added a listener and a handler to the TabbedViewNavigator. The event is dispatched from inside the active ViewNavigator view. The event has bubbles=true so that it goes up to the top of the event chain. Then just push the new view in the event handler.
Main View:
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" title="HomeView">
(...)
private function setup(){
tabbed_panel.addEventListener(CustomEvent.BUTTON_PRESSED, eventHandler);
}
private function eventHandler(ev:CustomEvent):void{
navigator.pushView(MyNewView,ev.event_data);
}
(...)
<s:TabbedViewNavigator id="tabbed_panel" width="100%" height="100%">
<s:ViewNavigator label="1st Tab" width="100%" height="100%"
firstView="views.FirstTabView"/>
<s:ViewNavigator label="2nd Tab" width="100%" height="100%"
firstView="views.SecondTabView"/>
</s:TabbedViewNavigator>
(...)
</s:View>
FirstTabView:
<View...>
(...)
private function buttonClickedHandler(event:Event):void{
dispatchEvent(new CustomEvent(event_data,"BUTTON_PRESSED",true));
}
(...)
</View>
I don't know if this is a corrrect approach to the problem, but it works.

Related

Null Object Reference after creating the Object

I have one MXML File as
<objecthandles:ObjectHandles xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" backgroundAlpha="1" xmlns:mx="library://ns.adobe.com/flex/mx"
allowRotate="true" minHeight="25" minWidth="60" height="100" width="200">
<s:BorderContainer id="borderCon" width="100%" height="100%" minHeight="25" minWidth="60"
backgroundAlpha="0" borderVisible="false" borderAlpha="0">
<s:HGroup id="hgText" width="100%" height="100%" gap="0" >
<mx:TextArea id="txtAdd" width="100%" height="100%" color="black"
minHeight="25" minWidth="60" horizontalScrollPolicy="off" verticalScrollPolicy="off" focusOut="txtAddKeyUpHandler(event)"
keyUp="txtAddMirrorKeyUpHandler(event)"
creationComplete="onTextCreationComplete()" />
</s:HGroup>
</s:BorderContainer>
</objecthandles:ObjectHandles>
When ever i create the object of the Fileas
var txtElement:TextElement = new TextElement();
txtElement.txtAdd.text = "Hello";
Then it showing the null object reference that
txtElement.txtAdd seems to be null
Need Perfect Solution?
In the Flex component lifecycle subcomponents will not be created until the parent component is added to the displaylist. Only when the component is added to the displaylist and fully built, will you be able to access its subcomponents. When the component is completely ready for usage, it will dispatch a FlexEvent.CREATION_COMPLETE event.
So do something like this:
var txtElement:TextElement = new TextElement();
txtElement.addEventListener(FlexEvent.CREATION_COMPLETE, initTxtElement);
addElement(txtElement);
private function initTxtElement(event:FlexEvent):void {
txtElement.txtAdd.text = "Hello";
}
Or better yet, since it's a custom component: expose the 'text' property as a property of 'TextElement' and handle the deferred setting of the property internally, so that you can write: txtElement.text = "hello".

Switch between Flex Tabbed ViewNavigators

I am working on a Flex TabbedViewNavigatorApplication with three tabs (ViewNavigator elements). I would like to switch from one ViewNavigator to another based upon a user action (via ActionScript code).
I know that switching between Views uses pushView and popView, but I'm working with ViewNavigators, and my searching revealed nothing useful.
I'm trying to switch from Tab2 to Tab1 when an event occurs. In this case, Tab2 contains a list, and when the user makes a selection, I want to jump back to Tab1.
<s:TabbedViewNavigatorApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
creationComplete="onAppReady(event)">
<s:ViewNavigator label="Tab1" width="100%" height="100%" firstView="views.TabOneView"/>
<s:ViewNavigator label="Tab2" width="100%" height="100%" firstView="views.TabTwoView"/>
<s:ViewNavigator label="Tab3" width="100%" height="100%" firstView="views.TabThreeView"/>
</s:TabbedViewNavigatorApplication>
Thanks for your help!
I use the following line of ActionScript to switch from one ViewNavigator to another based on a user action:
TabbedViewNavigator(navigator.parentNavigator).selectedIndex = 1;
It worked like a charm and seems simpler than bubbling events.
This class is strangely undocumented. I have not tried this myself, but from searching online, this is what I found which corroborates with what the rest of the network does.
What you need to do is bubble an event to the TabbedViewNavigatorApplication and from there change the selectedIndex property to whichever tab you need to change to. For example:
<s:TabbedViewNavigatorApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
creationComplete="onCreationComplete()">
<fx:Script>
<![CDATA[
private function onCreationComplete():void
{
this.addEventListener('someEvent', someHandler);
}
private function someHandler(e:Event):void
{
this.selectedIndex = 0; // or whatever index you want.
}
]]>
</fx:Script>
<s:ViewNavigator label="Tab1" width="100%" height="100%" firstView="views.TabOneView"/>
<s:ViewNavigator label="Tab2" width="100%" height="100%" firstView="views.TabTwoView"/>
<s:ViewNavigator label="Tab3" width="100%" height="100%" firstView="views.TabThreeView"/>
</s:TabbedViewNavigatorApplication>
You just need to dispatch a bubbling event from within your children. You could event create a custom event that holds data about which tab to switch to.

Dragging the AIR application window around the screen

I have an AIR application. It should be moved around the screen with the mouse. In order to achieve this I use the event:
this.stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown, true,-2);
It should be activated with the lowest priority compared to inserted elements for example those that should be scrolled, clicked, etc.
I tried the solution shown below with the event priority set to -1 because there might happen 2 different events and my moving application event should be the last one to be serviced or shouldn't be serviced at all.
<?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"
width="200"
height="200"
applicationComplete="init()">
<fx:Script>
<![CDATA[
import mx.core.Window;
import mx.events.ScrollEvent;
private function init():void {
this.stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown, true,-2);
}
private function onMouseDown(event:MouseEvent):void {
trace("clicked on stage "+event.currentTarget.toString());
if(event.currentTarget == stage){
trace("catched stage target");
this.nativeWindow.startMove();
event.stopImmediatePropagation();
}
}
function scrolledCanvasHandler(event:ScrollEvent){
trace("clicked on canvas "+event.currentTarget.toString());
event.stopPropagation();
}
]]>
</fx:Script>
<mx:Canvas x="29" y="34" width="80%" height="80%" backgroundColor="#343434" scroll="scrolledCanvasHandler(event)">
<mx:Label x="25" y="77" text="moving window, moving window"
fontSize="18" color="#FFFFFF" fontWeight="bold"/>
</mx:Canvas>
</s:WindowedApplication>
As you will notice the
event.stopPropagation();
doesn't work.
Perhaps my solution isn't the best suited to achieve this. Are there better solutions?
Chris
that's what i did in an app of mine:
<s:HGroup id="appTitleBar"
width="100%" height="35"
styleName="titleBar"
mouseDown="nativeWindow.startMove();"
doubleClickEnabled="true"
doubleClick="nativeWindow.minimize();"
contentBackgroundColor="#313131"/>
click (+drag) on this HGroup will drag the window. duobleclick will minimize it.
edit
don't make your whole app draggable this will only confuse the user.
and btw priority should be positive not negative - but also don't mess with this. not expected behavior for anyone.

TextArea component is null on applicationComplete event

I have a weird issue (weird because it is specific to one component) with applicationComplete in a fairly simple application. All the UI components are declared in MXML. I can access them all in applicationComplete, but not a spark.components.TextArea component, named taStatus here; it is null in the handler.
MXML looks sort of like this (there are lots of other components, but nothing special)
<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="710" minHeight="640" applicationComplete="onApplicationComplete(event)" width="710" height="640">
<mx:TabNavigator left="15" right="15" top="15" bottom="340" paddingTop="0">
<s:NavigatorContent label="General" width="100%" height="100%">
<s:Label x="93" y="71" text="Label" id="lblTest"/>
</s:NavigatorContent>
<s:NavigatorContent label="Status" width="100%" height="100%">
<s:TextArea id="taStatus" width="100%" height="100%" text="Startup." editable="false"/>
</s:NavigatorContent>
</mx:TabNavigator>
<fx:Script source="main.as" />
</s:Application>
Here is the handler in main.as
protected function onApplicationComplete(event: FlexEvent) : void
{
lblTest.text = 'abc789'; // OK
taStatus.text = 'abc789'; // Fail
}
TypeError: Error #1009: Cannot access a property or method of a null object reference. So taStatus is null... What is so special about this TextArea?
Update 2010-06-12 02:53
Moving the NavigatorContent (tab) above all other tabs suddenly makes the TextAreas get instantiated on time. Very strange, because all the components are definitely being created; I can see them.
It's because the TextArea is in a child of the TabNavigator that is not the first child, so by default it is not instantiated until the user opens that tab.
Your options are to either wait until the user opens that tab to do whatver you need to do to set up the TextArea or change the child creation policy on the TabNavigator to create all its children at startup rather than waiting for them to be clicked.
To do that, you need to set the creationPolicy property on the TabNavigator to "all".

Capturing key events in a mx:Image

I'm trying to capture key events in a mx:Image and I can't get it to work.
<?xml version="1.0" encoding="utf-8" ?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" />
<mx:Canvas width="100%" height="100%">
<mx:Label id="lab" x="50" y="20" text="Nothing"/>
<mx:Image x="50" y="50" source="#Embed('image.png')" keyDown="lab.text='Something';"/>
</mx:Canvas>
</mx:Application>
When I press a key when the mouse is over the image I expect the label text to change to "Something", but nothing happens. I've done all sorts of combination of enabled and putting the keyDown on the canvas and the label as well.
What am I missing?
The issue is one of focus. Key down events are only generated within a component when that component (or one of its descendants) has focus. In order for an Image to receive focus, you must set focusEnabled to true. This, however, requires that the user tab to give the Image focus, since clicking on an Image does not convey focus, much less mousing over it.
If you want to listen for the key down event when the user's mouse is over the Image, you can manually assign focus to the Image when the user moves their mouse over it.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Script>
<![CDATA[
import mx.core.UIComponent;
import mx.managers.IFocusManagerComponent;
private var oldFocus:IFocusManagerComponent;
public function imageMouseOver(event:MouseEvent):void {
oldFocus = focusManager.getFocus();
var newFocus:UIComponent = event.target as UIComponent;
newFocus.setFocus();
}
private function imageMouseOut(event:MouseEvent):void {
if (oldFocus) {
oldFocus.setFocus();
}
}
]]>
</mx:Script>
<mx:Canvas width="100%" height="100%">
<mx:Label id="lab" x="50" y="20" text="Nothing"/>
<mx:Image x="50" y="50" source="#Embed('image.png')" mouseOver="imageMouseOver(event)" mouseOut="imageMouseOut(event)" keyDown="lab.text='Something';" focusEnabled="true"/>
</mx:Canvas>
</mx:Application>
Alternately, you can assign a key down listener to the stage when the user mouses over the Image, and remove it when the user mouses out.
Image derives from (a.k.a "is a") SWFLoader. You need to add listeners to the content of the loader, not the loader itself. See this question for details, Interact with SWF Loader

Resources