Search code examples
flashapache-flexflash-builder

viewMenuItems doesn't appear when applying skin to ViewNavigatorApplication


ViewMenuItems works fine when I don't apply skinClass fo ViewNavigatorApplication, but when I apply custom skin to ViewNavigator, ViewMenuItems doesn't appear.

My Main.mxml file contains

    <?xml version="1.0" encoding="utf-8"?>
    <s:ViewNavigatorApplication xmlns:fx="http://ns.adobe.com/mxml/2009"                                  xmlns:s="library://ns.adobe.com/flex/spark" firstView="views.MainHomeView"                                skinClass="skins.CustomApplicationSkin">
        <fx:Declarations>
            <!-- Place non-visual elements (e.g., services, value objects) here -->
            </fx:Declarations>
    </s:ViewNavigatorApplication>

Code for CustomApplicationSkin is

<?xml version="1.0" encoding="utf-8"?>
<s:Skin name="CustomApplicationSkin"
            xmlns:fx="http://ns.adobe.com/mxml/2009"
                     xmlns:s="library://ns.adobe.com/flex/spark"
                               >

    <fx:Metadata>
                <![CDATA[ 
                [HostComponent("Main")]
                ]]>
            </fx:Metadata> 

    <s:states>
                <s:State name="normal" />
                 <s:State name="disabled" />
             </s:states>

    <s:BitmapImage width="100%" height="100%" source="@Embed('/assets/background.jpg')"/>

    <s:Group id="contentGroup" width="100%" height="100%" minWidth="0" minHeight="0" />

    <s:ViewNavigator id="navigator" width="100%" height="100%" />

</s:Skin>

Code for MainHomeView.mxml

<?xml version="1.0" encoding="utf-8"?>
<!-- components\mobile\views\ViewMenuHome.mxml -->
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009" 
        xmlns:s="library://ns.adobe.com/flex/spark" 
        title="Home">

    <fx:Script>
        <![CDATA[
        // The event listener for the click event.
            private function itemClickInfo(event:MouseEvent):void {
                switch (event.currentTarget.label) { 
                    case "Add" : 
                        myTA.text = "Add selected"; 
                        break; 
                    case "Cancel" : 
                        myTA.text = "Cancel selected"; 
                        break; 
                    case "Delete" : 
                        myTA.text = "Delete selected"; 
                        break; 
                    case "Edit" : 
                        myTA.text = "Edit selected"; 
                    break; 
                case "Search" : 
                    myTA.text = "Search selected"; 
                        break; 
                default : 
                    myTA.text = "Error"; 
                }                
            }
        ]]>
    </fx:Script>

    <s:viewMenuItems>
        <s:ViewMenuItem label="Add" click="itemClickInfo(event);" />
        <s:ViewMenuItem label="Cancel" click="itemClickInfo(event);"/>
        <s:ViewMenuItem label="Delete" click="itemClickInfo(event);"/>
        <s:ViewMenuItem label="Edit" click="itemClickInfo(event);"/>
        <s:ViewMenuItem label="Search" click="itemClickInfo(event);"/>
    </s:viewMenuItems>

    <s:VGroup paddingTop="10" paddingLeft="10">
        <s:TextArea id="myTA" text="Select a menu item"/>
        <s:Button label="Open Menu" 
              click="mx.core.FlexGlobals.topLevelApplication.viewMenuOpen=true;"/>
        <s:Button label="Close Menu" 
              click="mx.core.FlexGlobals.topLevelApplication.viewMenuOpen=false;"/>
    </s:VGroup>
</s:View>

Please Assist.


Solution

  • There are two problems:

    1. Your skin is not including the menu (a ViewMenu object)
    2. The ViewNavigatorApplicationSkin class is written in Actionscript and extends the MobileSkin class, while your skin is written in MXML and extends the Skin class.

    The first problem is easy to solve: I added an "initialize" event handler to your skin, wherein I instantiated the ViewMenu object in the same way it's done in the constructor for the default mobile skin (ViewNavigatorApplicationSkin).

    However, after doing that, I get an error at run time when trying to open the view menu. This error is a result of the second problem above: specifically, the code in ViewNavigatorApplication is trying to use the addChild() method to add the menu, however, since your custom skin extends the non-mobile Skin class (which is essentially a Spark Group), it throws an error saying you can't use the addChild() method, and should use addElement() instead. This error is a direct result of basing your skin on the non-mobile skin class (the host component, ViewNavigatorApplication, is not expecting that).

    I would recommend you create your skin in Actionscript, by extending the original ViewNavigatorApplicationSkin class. It seems all you're doing is adding a background, so something like the code below might work. However, the problem is that in your skin the ViewNavigator and the background image are both 100% in width/height. So the navigator completely covers the background. In my example below, I gave the navigator some transparency to let the background show through. I'm assuming you won't want that, but in your current design that's the only way to see the image.

    package
    {
        import spark.components.Image;
        import spark.skins.mobile.ViewNavigatorApplicationSkin;
    
        public class MyViewNavAppSkin extends ViewNavigatorApplicationSkin
        {
            public function MyViewNavAppSkin()
            {
                super();
            }
    
            [Embed('Android_Robot_100.png')]
            private var embeddedImage:Class;
    
            private var background:Image;
    
            override protected function createChildren():void
            {
                super.createChildren();
                background = new Image();
                background.source = new embeddedImage();
                addChildAt(background, 0);
                navigator.alpha=.5;
            }
    
            override protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void
            {
                background.setLayoutBoundsSize(unscaledWidth, unscaledHeight);
                background.setLayoutBoundsPosition(0,0);
                super.layoutContents(unscaledWidth, unscaledHeight);
            }
        }
    }