Flex Mobile Development – Tabbed Applications with Effects on Tab Change

August 22, 2011By 36 Comments

Flex 4.5 includes new ViewTransition classes which I’ve blogged about previously and can be used to apply an effect to your view navigation within a ViewNavigator. However, you cannnot use the ViewTransition classes when switching between tabs since you are no longer within the same ViewNavigator but between ViewNavigators. There is still a way to apply similar effects to what you would see when using a ViewTransition however, and one way is shown here…

First you need to add an IndexChangeEvent handler to your implicit tabbedNavigator property in your TabbedViewNavigatorApplication on applicationComplete:

protected function tabbedviewnavigatorapplication1_applicationCompleteHandler(event:FlexEvent):void
{
    this.tabbedNavigator.addEventListener(IndexChangeEvent.CHANGE,onChange);
}

This event is dispatched when a tab is selected so you can perform any necessary handling here. In this case I want to apply a Spark Effect to my View that is being selected. I can check the newIndex property on the IndexChangeEvent and play different effects for each tab if desired as well. First consider these effects I’ve defined in the root application MXML:

<fx:Declarations>
		<s:Sequence id="seqEffect">
			<s:Parallel>
				<s:Scale duration="800" id="scaleUp" scaleXBy=".8"/>
				<s:Rotate3D angleYFrom="0.0" angleYTo="360" 
							duration="1600"
							repeatCount="{2}" repeatBehavior="reverse"/>
			</s:Parallel>
			<s:Scale duration="300" id="scaleDown" scaleXBy="-.8"/>
		</s:Sequence>
		<s:Move3D id="moveEffect" duration="300" xFrom="400" xTo="0"/>
		<s:Fade duration="800" id="fadeEffect" alphaFrom="0" alphaTo="1.0"/>
</fx:Declarations>

Note: If you’re not familiar with all of the Spark Effects, check out Tour de Flex under Flex 4 -> Components -> Effects to see various samples of how to use them all.

Now in the following handler, I simply check the event index and set the target of the effect to the matching ViewNavigator’s activeView property:

protected function onChange(event:IndexChangeEvent):void
{
	if (event.newIndex == 0)
	    seqEffect.play([v1.activeView]); 
	else if (event.newIndex == 1)
	    moveEffect.play([v2.activeView]); 
	else if (event.newIndex == 2)
	    fadeEffect.play([v3.activeView]); 
}

Since I’m handling the IndexChangeEvent.CHANGE event and not the IndexChangeEvent.CHANGING event, I can be sure the activeView has been set to the new ViewNavigator’s activeView. When the effects are played you will see a transition of sorts similar to what you might see when using a ViewTransition. You can see that you could also create more complex effects using a sequence or parallel set of effects, and even add an easing function if desired to create different possibilities. To simply mimic the ViewTransition classes, you would likely use the Spark Fade, Move, Rotate3D and Scale effects alone to show something similar to the CrossFadeViewTransition, SlideViewTransition, FlipViewTransition and ZoomViewTransition accordingly. The code above will cause the effect to apply to the View itself, similar to how the ViewTransition’s work by default, however you could also play the effect to include the actionBar as well by coding this instead:

protected function onChange(event:IndexChangeEvent):void
{
    if (event.newIndex == 0)
	    seqEffect.play([v1]); 
	else if (event.newIndex == 1)
	    moveEffect.play([v2]); 
	else if (event.newIndex == 2)
	    fadeEffect.play([v3]); 
}

My full source for the main TabbedViewNavigatorApplication class looks like this:

<?xml version="1.0" encoding="utf-8"?>
<s:TabbedViewNavigatorApplication xmlns:fx="http://ns.adobe.com/mxml/2009" 
				xmlns:s="library://ns.adobe.com/flex/spark" applicationComplete="tabbedviewnavigatorapplication1_applicationCompleteHandler(event)">
	
	<fx:Style source="styles.css"/>
	<fx:Script>
		<![CDATA[
			import mx.events.FlexEvent;
			
			import spark.events.IndexChangeEvent;
			
			import views.SlideView;
			
			protected function tabbedviewnavigatorapplication1_applicationCompleteHandler(event:FlexEvent):void
			{
				this.tabbedNavigator.addEventListener(IndexChangeEvent.CHANGE,onChange);
			}
			
			protected function onChange(event:IndexChangeEvent):void
			{
				if (event.newIndex == 0)
					seqEffect.play([v1.activeView]); 
				else if (event.newIndex == 1)
					moveEffect.play([v2.activeView]); 
				else if (event.newIndex == 2)
					fadeEffect.play([v3.activeView]); 
			}
		]]>
	</fx:Script>
	
	<fx:Declarations>
		<s:Sequence id="seqEffect">
			<s:Parallel>
				<s:Scale duration="800" id="scaleUp" scaleXBy=".8"/>
				<s:Rotate3D angleYFrom="0.0" angleYTo="360" 
							duration="1600"
							repeatCount="{2}" repeatBehavior="reverse"/>
			</s:Parallel>
			<s:Scale duration="300" id="scaleDown" scaleXBy="-.8"/>
		</s:Sequence>
		<s:Move3D id="moveEffect" duration="300" xFrom="400" xTo="0"/>
		<s:Fade duration="800" id="fadeEffect" alphaFrom="0" alphaTo="1.0"/>
	</fx:Declarations>
	
	<s:ViewNavigator id="v1" label="Welcome" width="100%" height="100%" firstView="views.WelcomeView" activate="seqEffect.play([v1.activeView])"/>
	<s:ViewNavigator id="v2" label="Slide View" width="100%" height="100%" firstView="views.SlideView" />
	<s:ViewNavigator id="v3" label="Fade View" width="100%" height="100%" firstView="views.FadeInView" />
	
</s:TabbedViewNavigatorApplication>

Note: If you want to play an effect initially when the application is run on the first view, you can also add an activate event handler on the ViewNavigator and play the effect on the active view in the same manner, such as:

<s:ViewNavigator id="v1" label="Welcome" width="100%" height="100%" firstView="views.WelcomeView" activate="effect1.play([v1.activeView])"/>			

Here’s a short 28 second video of this running on my iPhone 4:

Full source for this project can be downloaded HERE.


Filed in: Flex 4.5Flex 4.5/MobileFlex iOSFlex Mobile Tags:

About the Author ()

Comments (36)

Trackback URL | Comments RSS Feed

  1. Mike says:

    Very Cool! Perfect timing. I’m going to include these into my prototype I’m presenting to my mangers in our organization.

    Thanks for sharing.

  2. vic says:

    When people say: Flash runs slow on mobile, this is why.

    Consider showing how to write fast running code by using pure .as, that would make flash run fast on mobile. Start w/ a sprite, use signals, etc.

    • Hi Vic, thanks for your input. It is true that using pure AS is always going to be faster on mobile, thank you for pointing that out. If you have a sample you would like to share with everyone that would be great, thanks! Holly

  3. vic says:

    I do have sample of pure .as on Apple’s IOS mobile and one for Android.
    To see them:
    npm install gamina (but npm is tricky to install, still worth it)

    I had to write my own components to avoid the heavy MXML:
    https://github.com/puppetMaster3/Gamina-Core/tree/master/asSrc/org/gamina/elements (free to fork)
    These things can do more than flex’s limited item render, like smooth scrolling.

    I’d be happy to corroborate w/ you or anyone to show how to write high performance flash just to make flash look better to the public. Else… its big and slow w/ low development productivity.

  4. I was told by someone at Adobe that the MXML is converted to AS anyway so MXML wouldnt slow it down unless you used it for skins.

  5. I’m not so convinced that the approach taken in these tutorials for Flex Mobile are hurting performance. I started a conversation on Google+ after reading this and have gotten some pretty interesting responses. Some from Adobe personal explaining that MXML is fine and won’t hinder your app.
    Feel free to chime in.
    https://plus.google.com/104551464417427348669/posts/hnr1kCezUv4

  6. vic says:

    That someone at Adobe needs some more experience.

    MXML is a code generator for .as, it writes lots of crappy code.
    If you write your own code, it’s smaller and faster. MXML makes Flex and flash look bad on mobile.

  7. michael B says:

    @vic
    MXML is a “mark-up” for as3. I have written some fairly forward applications in MXML and its runs great!
    MXML and the flex framework saves you tons of time from a developer stand point. Its not a bad thing to use vs. strict as3, all depends what you are trying to do I guess…. BTW : Holly – nice vid, how do you code with those nails? …LOL

    respect,
    mB

    • Hi Michael! Thanks, and yes, the Flex framework does save a ton of time from a developer standpoint! I think in the end the whole debate with MXML vs AS3… it really just depends on what you’re trying to accomplish…

      And the nails… lol, I guess I’ve been doing this so long I probably wouldn’t be able to code without them! Does cause me to be a bit noisier than the average programmer though ;-)

      Cheers, Holly

  8. Vic says:

    Micheal B:
    Thx but I do know MXML. (and I could ask you the same, do you know how to write a pure .as app?). I found that it takes time to code itemrender and etc.

    I believe that anything that can be done in mx can be done better in yui, mochaUI, Dojo, jxLib, etc.
    But .as > .js, just as .js > mx.
    So when people only show MX, it makes Adobe Flash look bad.

    In order to find out which is more productive, .as or mx, I think people that can write an app in either should have a voice.

  9. Bryan says:

    Holly,

    I noticed your example video shows one of the things I am hoping to fix: When you are using tabbed navigation and the user attempts to input text, the keyboard pushes the tabs up into the middle of the view rather than coming up over them. I have a couple of ideas of how I am going to overcome this (hide them on invocation of the keyboard and show them again when it is dismissed, etc) but wondered if there was a ‘best practices’ or simpler way of doing it.

    Also in regards to the tabs, is there a way to push data into an individual tab and skin it independently (to show a little round ‘you have 3 messages’ badge for example)?

    Thanks for all the great tutorials.

    • Hey Bryan! Try adding the resizeForSoftKeyboard=”false” property to the root TabbedViewNavigatorApplication tag and see if that gives you what you’re looking for, I believe it is.

      As far as your other question with the tabs and skinning… Are you asking if you can change the look of just one tab and only based on a certain context? You can skin your tabs with different skins if you prefer. Take a look at the TabbedViewNavigatorTabBarSkin.as class in the Flex SDK mobile skins and you should see more what I’m talking about. You could change the skins that are used in there to be different for the first button, last etc. I have another post here on my blog about skinning tabs as well that might be useful:

      http://devgirl.org/2011/05/09/styling-the-flex-4-5-mobile-application-tabs-and-actionbar/

      Let me know if I misunderstood what you’re asking…

      Hope it helps!
      Holly

      • Bryan says:

        Thanks for your reply.

        I am actually trying to call methods within, pass data into (or bind custom attributes within) individual tabs to (for example) show when there have been updates within a certain tab.

        Similar to the red circle with the number 2 on this button (but on a tab):

        http://www.iphonetechzone.com/wp-content/uploads/2009/07/iphone_push.jpg

        In a more general sense I am just trying to figure out how to set custom variables in a specific tab. Something like:

        myTabs.specificTab.showBadge(2);

        or

        myTabs.specificTab.numMessages = 3;

        or

        myTabs.otherTab.showBusyCursor();

        which I assume would change the state of the individual tab’s skin…

        Thanks! B

      • Mike says:

        Hi Holly – I am having the same issue with the soft keyboard and StageWebView on the iPad. When the keyboard opens it pushed up the ActioBar off of the screen and it doesn’t come back when it closes. If I rotate the screen I can see it for a brief monment in the corner then it is gone off screen. I tried adding resizeForSoftKeyboard=”false” as you mentioned above but I still have he same results.

        Thanks in advance
        Mike

  10. mihau says:

    Hi, i have some strange problem, i have now idea how to trigger tab navigator action from button…

    I’ll try to explain:
    i have tabbed application and 4 tabs: <s:ViewNavigator …

    if ii click on tab my view is changing – so it's ok.

    but i want to change to this view by pressing some button inside application.

    if im using pushView my tab who have set this firstView won't set to active, why?

    So im trying to set "Active tab" manually by setting FlexGlobals.topLevelApplication.tabbedNavigator.selectedIndex = 2;

    If i manualy set navigator index to 2, my first tab is not active but its not response for clicking – its not changing to its basic firstView.

    Its really quite strange, probably i doesnt understand tabs.

  11. Secret007 says:

    Thanks Holly for this great tutorial, i like your blog, there are many useful code snippets and also i m learning as well :P

    @bmanderscheid: That adobe person is right, MXML in the end converts into AS, and yes if you use mxml for skinning than your mobile app will be heavy depends on how much stuff you skin… Obviously MXML is simple and time saving…

    • For more interesting comments/discussion regarding using MXML vs AS3, check out this link to Greg Wilson’s blog…

      http://gregsramblings.com/2011/08/25/the-flex-team-discusses-the-future-of-flex/

    • Kostas says:

      The issue is not whether MXML code ultimately (more presicely, at an intermediate stage) converts to AS. It definitely does. But the point that vic wants to make, I guess, is that *that* AS, like more or less any other mechanically generated code, ends up being awfully bloated and, as a consequence, inneficient compared to *your* AS, handcrafted and having only the bare minimum required functionality. As an example consider generating html from Word: A net «Hello World!» written by hand is 192 bytes, the filtered Word 97 version is 909 bytes (nests a SPAN in a P, and the P in a DIV, not caring that we only have one paragraph of two words), and the full Word 97 version, with all the MS and Office namespaces etc., which primarily help this file re-open in Word without having lost any of the Office features, is 21.870 bytes! Two words and an exclamation point!

  12. mihau says:

    Hi Holly,
    i know what i was doing wrong,
    im trying to push view in my tabbed application (trying to go from one tab to another using button), …

    I just should set directly required tab index:
    FlexGlobals.topLevelApplication.tabbedNavigator.selectedIndex = 2

    I thinking about changing views not about tabs

  13. mihau says:

    Almost :)

    I’m searching how to set view title visible to false on this tabbedNavigator application.

    I was search allover the internet and i just can find how to hide tabBar.

    I woudl like to hide titleBar depands if its landscape or portait mode.

    Is it possible ?

  14. mihau says:

    ya right – thanks offcoz its works :)
    but how make it global ?

    maybe it will be some idea to using Parsley Dependency Injection – i think it could work

  15. Saidi Reddy says:

    Hi i have one query could u please help me , Panel control not supported for Flex Mobile Project in Flash builder 4.5. There is any alternate control to add some data dynamically (using Actin Script). please help me thanks in advance …

  16. Frank says:

    Hi Holly,

    I was wondering if it is possible to rearrange the single ViewNavigator elements using drag and drop.

    Is that at all possible, cause I couldn’t find anything related to that.

    Cheers and greetings from the cloudy UK! Frank

  17. Adarsh says:

    Hi… Please Reply, m developing a Apple IPAD app (FB 4.5.1 and Win 7) and hv made Text input corners round using skinning([HostComponent("spark.components.TextInput")]).But when m running this app on Ipad,then textinput is unable to call the softkeyboard. please reply asap.

    Thank You.

  18. Edy says:

    Hi Holly. Thanks a lot for this and other tutorials you have. They have helped me a lot.
    I have the following situation: I have a TabbedViewNavigatorApplication with some ViewNavigators each one of them containing a view. I want to swipe from one view to another view but the move3d effect I’m applying affects only the new View. As result the actual view disappears (with no effect) and the new view appears with the desired effect. How can I apply an effect to the old view so that both effects take place at the same moment?

  19. Edy says:

    I want to simulate a SlideViewTransition but i don’t know how.

  20. Barry says:

    Hi :)

    I have a small TabbedViewNavigatorApplication with 5 ViewNavigator buttons.

    I tried to use 1 button to refresh data, using a click event and defining no firstView. Didn’t work :(

    Is there a way to use a ViewNavigator button just to start an as3 function?

    Thanks
    Barry

  21. cybercow says:

    protected function onChange(event : IndexChangeEvent) : void {
    fadeEffect.play((this.tabbedNavigator.navigators[event.newIndex] as ViewNavigator).activeView);
    }

    :)

    • Barry says:

      thx :-)
      I’ll give it a try!

      • cybercow says:

        Sorry i forgot the square brackets for the play method, as it takes Array as argument so it should look like:

        ..
        fadeEffect.play([(this.tabbedNavigator.navigators[event.newIndex] as ViewNavigator).activeView]);
        ··

        This is to spare multiple if statements in the situations we want only one effect, (eg. fadeEffect) which i liked the most :) btw. great article and examples!

Leave a Reply