7 Replies Latest reply on Jan 3, 2012 2:16 PM by alinator11

    Layout on iOS differs from Layout on Android

    alinator11 Level 2

      I am getting (remaining) very frustrated with my choice to use Flex and Adobe products for my mobile app. Adobe making SWFLoader an available object in mobile WITHOUT TELLING US IT WON'T WORK IN PRODUCTION cost me a lot of time and money. My last post here was at the beginning of November when I believed I was about ready to deploy my app but I've had to take all this time to re-write it given that SWFLoader was added to the dist just to make it possible for programmers to really go the wrong direction. Sorry to be so annoyed but honestly, this has cost me a lot of money and I don't have the rest of my life to screw around with software that does not do what the vendor says it will do.

       

      In any event, my latest frustration, now that I'm back to being done with all the re-coding aspects of the app, is getting it to lay out on screen properly. On my Android device, I just add the flex component to the screen, find the registration point, center on that and it lays out properly. In iOS, it does not work. Here is an example of laying out a screen with a center registration point.

       

      var xPixels:uint = Capabilities.screenResolutionX;

      var yPixels:uint  = Capabilities.screenResolutionY;

                      if(xPixels < yPixels) {

                          //phone is tilted and we're measuring the incorrect orientation.

                          //reverse x and y and double them to get proper screen dim.

                          var yPix:uint = xPixels + xPixels;

                          xPixels = yPixels + yPixels;

                          yPixels = yPix;

                      }               

                      //the following will be sure that we've centered the

                      //screen.

                      xPixels = xPixels / 2;

                      yPixels = yPixels / 2;

       

                     this.busyLoader = new LoaderMessageCurtain;

                      busyLoader.x = xPixels;

                      busyLoader.y = yPixels;               

                      enemyViewContainer.addChild(busyLoader);

                      loaderShowing = true;

       

       

      In the above code, busyLoader was created in FlashPro (as all my screens and animations are). It was converted to a Flex component. It has  registration at the center of the screen. On Android, this code causes the screen to lay out properly. On iOS, I'll get the top left corner in the center of the screen. WHY?????????????? WHY, WHY, WHY? I'm sick of it. Can anyone help me understand the voodoo that is going on behind the scenes to actually be done with this project with a professional looking piece of software that behaves as expected on ALL platforms it is touted as being able to be run on?

       

      Notice as well that on Android, I don't have to tell busyLoader what dimensions it is. That's great. However, on iOS, if I don't set the width and height of the component, it will be giant or super small but not properly sized in either case. When I do set the dimensions, the outcome is problematic. Sometimes it fits and sometimes I need to add or subtract an arbitrary number of pixels to get it size properly. Sigh.

       

      Also, I write software on a large monitor. Using the emulators to lay out code is a total waste. If I use the iPhone 4 emulator, for instance, it reports that my screen size is 1900 by 1200. Well, my monitor is 1900 by 1200 but an iPhone is not (it's 960 by 640 if I recall). It does me no good to have an emulator lay out based on my screen size. That too has cost me dearly. No offense and your tools are cool and I hope that I'll be able to solve my frustrations with Flex and continue to use your tools, but why don't you guys try doing what I do before I release software... make sure it works?

       

       

       

       

       

       

       

       

       

       

       


        • 1. Re: Layout on iOS differs from Layout on Android
          drkstr_1 Level 4

          1) Never use a center registration point. This adds all kinds of craziness when working in actual code. Always register top left, as anything else is just a "hack" to make the Flash IDE easier to use for designers.

           

          2) You are going about the layout in the wrong way. Screen capabilities are for when you need to determine the actual screen size (like going into fullscreen mode video for example). Where is this code you posted being executed? Any layout logic should be preformed in the updateDisplayList method using the provided dimensions to determine your size (old-style), or by using a custom Layout class (new-style, also usually not needed as there are many built in layouts to handle this kind of stuff).

           

          3) Why are you not just setting horizontalCenter and verticalCenter to 0? This will center the object in whatever IVisualElementContainer it is sitting in (assuming it has the default BasicLayout assigned).

          • 2. Re: Layout on iOS differs from Layout on Android
            alinator11 Level 2

            Thanks so much for the reply. And, sorry for being frustrated. I really do want all this investment in learning Adobe tools to work out but there have been some difficulties along the way to be sure.

             

            The reason the objects have a center registration is because the animator was trying to ensure that the animations work for different screen sizes without changing the length to width ratio of the animations. He drew a black border around the entire stage. On platforms where the screen does not fill the stage properly, the black border shows up at bottom. While I will make sure that he keeps them registered at top left from now on, I don't see why that makes any difference. A center point is a center point and should work as well as any other point on a screen (I would think).

             

            I'm very interested in your thoughts on the Capabilities.screenResolutionX and Y function. Given that screens are not laying out properly without explicitly setting their size, and I want them to be the size of the screen in question (it's a full-screen mobile app), I have no choice but to call Capabilities.screenResolution X and Y and set myFlexComp.w and h as these values. If there is a better way to do this, please point me toward the resource so I can learn about it.

            • 3. Re: Layout on iOS differs from Layout on Android
              drkstr_1 Level 4

              The reason the objects have a center registration is because the animator was trying to ensure that the animations work for different screen sizes without changing the length to width ratio of the animations. He drew a black border around the entire stage. On platforms where the screen does not fill the stage properly, the black border shows up at bottom. While I will make sure that he keeps them registered at top left from now on, I don't see why that makes any difference. A center point is a center point and should work as well as any other point on a screen (I would think).

               

              Well the problem is that it requires you to know ahead of time how a particular asset is registered in order to properly layout and position it through code. Display objects are much easier to work with if you can assume they are always registered in their natural top left corner, and should not extend passed the desired width/height (IE. setting a negative x/y value to get a "border"). This allows the developer to know exactly where it should be positioned based on it's measured size, without having to worry about varying registration points.

               

              I'm very interested in your thoughts on the Capabilities.screenResolutionX and Y function. Given that screens are not laying out properly without explicitly setting their size, and I want them to be the size of the screen in question (it's a full-screen mobile app), I have no choice but to call Capabilities.screenResolution X and Y and set myFlexComp.w and h as these values. If there is a better way to do this, please point me toward the resource so I can learn about it.

               

              One should never assume the application fits the entire screen (as you have seen first hand). There are numerous better way to do this. It really depends on how your application is set up.

               

              Here is an example AIR app that shows one possible way these assets can be correctly imported and positioned within your application. If you run the app and resize the window, you will see that the icon will always remain centered and the background will always fill the size of the window (notice how the MainView_background symbol has 9-slice scaling enabled in the fla, which is how the border remains a uniform size regardless of the window size).

               

               

              <?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:views="views.*" width="1024" height="768">
              
              
                             <fx:Style>
                                            @namespace s "library://ns.adobe.com/flex/spark";
                                            @namespace views "views.*";
              
                                            views|MainView
                                            {
                                                           backgroundClass: Embed(source="assets.swf", symbol="MainView_background");
                                                           iconClass: Embed(source="assets.swf", symbol="MainView_icon");
                                            }
                             </fx:Style>
              
                             <views:MainView top="0" left="0" bottom="0" right="0" />
              
              </s:WindowedApplication>
              

               

               

               

               

               

              package views
              {
                             import flash.display.DisplayObject;
                             import mx.core.IFlexDisplayObject;
                             import mx.core.ILayoutElement;
                             import mx.core.UIComponent;
              
                             /**
                              * This class takes 2 styles for a background and icon class. The background is
                              * scaled to the size of the component, while the icon is centered within it.
                              *
                              * I also copied a few utility methods from the MobileSkin to show how the various
                              * class formats are supported.
                              *
                              */
                             public class MainView extends UIComponent
                             {
                                            public function MainView()
                                            {
                                                           super();
                                            }
              
                                            /**
                                             * Copied from spark.skins.mobile.supportClasses.MobileSkin
                                             */
                                            public static function getElementPreferredHeight(element:Object):Number
                                            {
                                                           if(element is ILayoutElement)
                                                           {
                                                                          return ILayoutElement(element).getPreferredBoundsHeight();
                                                           }
                                                           else if(element is IFlexDisplayObject)
                                                           {
                                                                          return IFlexDisplayObject(element).measuredHeight;
                                                           }
                                                           else
                                                           {
                                                                          return element.height;
                                                           }
                                            }
              
                                            /**
                                             * Copied from spark.skins.mobile.supportClasses.MobileSkin
                                             */
                                            public static function getElementPreferredWidth(element:Object):Number
                                            {
                                                           if(element is ILayoutElement)
                                                           {
                                                                          return ILayoutElement(element).getPreferredBoundsWidth();
                                                           }
                                                           else if(element is IFlexDisplayObject)
                                                           {
                                                                          return IFlexDisplayObject(element).measuredWidth;
                                                           }
                                                           else
                                                           {
                                                                          return element.width;
                                                           }
                                            }
              
                                            /**
                                             * Copied from spark.skins.mobile.supportClasses.MobileSkin
                                             */
                                            public static function setElementPosition(element:Object, x:Number, y:Number):void
                                            {
                                                           if(element is ILayoutElement)
                                                           {
                                                                          ILayoutElement(element).setLayoutBoundsPosition(x, y, false);
                                                           }
                                                           else if(element is IFlexDisplayObject)
                                                           {
                                                                          IFlexDisplayObject(element).move(x, y);
                                                           }
                                                           else
                                                           {
                                                                          element.x = x;
                                                                          element.y = y;
                                                           }
                                            }
              
                                            /**
                                             * Copied from spark.skins.mobile.supportClasses.MobileSkin
                                             */
                                            public static function setElementSize(element:Object, width:Number, height:Number):void
                                            {
                                                           if(element is ILayoutElement)
                                                           {
                                                                          ILayoutElement(element).setLayoutBoundsSize(width, height, false);
                                                           }
                                                           else if(element is IFlexDisplayObject)
                                                           {
                                                                          IFlexDisplayObject(element).setActualSize(width, height);
                                                           }
                                                           else
                                                           {
                                                                          element.width = width;
                                                                          element.height = height;
                                                           }
                                            }
              
                                            private var _background:DisplayObject;
              
                                            private var _icon:DisplayObject;
              
                                            override protected function createChildren():void
                                            {
                                                           super.createChildren();
              
                                                           var bgClass:Class = this.getStyle("backgroundClass") as Class;
              
                                                           if(bgClass != null)
                                                           {
                                                                          _background = new bgClass();
                                                                          this.addChild(_background);
                                                           }
              
                                                           var iconClass:Class = this.getStyle("iconClass") as Class;
              
                                                           if(iconClass != null)
                                                           {
                                                                          _icon = new iconClass();
                                                                          this.addChild(_icon);
                                                           }
                                            }
              
                                            override protected function measure():void
                                            {
                                                           super.measure();
              
                                                           //lets say our prefered dimensions are at least the size of the icon...
                                                           if(_icon)
                                                           {
                                                                          this.measuredWidth = getElementPreferredWidth(_icon);
                                                                          this.measuredHeight = getElementPreferredHeight(_icon);
              
                                                                          trace("this.measuredWidth = " + this.measuredWidth);
                                                                          trace("this.measuredHeight = " + this.measuredHeight);
                                                           }
                                            }
              
                                            /**
                                             * @private
                                             */
                                            override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
                                            {
                                                           trace("MainView.updateDisplayList(" + unscaledWidth + ", " + unscaledHeight + ")");
                                                           super.updateDisplayList(unscaledWidth, unscaledHeight);
              
                                                           //bet let's actually scale the contents to the full size as set by the parent...
                                                           if(_background != null)
                                                           {
                                                                          setElementSize(_background, unscaledWidth, unscaledHeight);
                                                                          setElementPosition(_background, 0, 0);
                                                           }
              
                                                           //and we will center the icon within those bounds...
                                                           if(_icon != null)
                                                           {
                                                                          var iconWidth:Number = getElementPreferredWidth(_icon);
                                                                          var iconHeight:Number = getElementPreferredHeight(_icon);
                                                                          var iconX:Number = (unscaledWidth - iconWidth) / 2;
                                                                          var iconY:Number = (unscaledHeight - iconHeight) / 2;
              
                                                                          trace("iconWidth = " + iconWidth);
                                                                          trace("iconHeight = " + iconHeight);
                                                                          trace("iconX = " + iconX);
                                                                          trace("iconY = " + iconY);
              
                                                                          setElementPosition(_icon, iconX, iconY);
                                                           }
                                            }
                             }
              }
              

               

               

              This methodology uses the appropriate stages in the flex component life-cycle to create, measure, and position its contents.

               

               

              *note: I was going to attach an example project, but I can't seem to figure out how to add an attachment. Send me a PM if you would like me to email it to you.

               

              *edited a few typos*

              • 4. Re: Layout on iOS differs from Layout on Android
                alinator11 Level 2

                Thanks for that. Sorry for the slow reply - I'm blaming the holidays and being out of town. I really appreciate your input and example object. Hopefully I can implement these changes and finally wrap this up.

                • 5. Re: Layout on iOS differs from Layout on Android
                  alinator11 Level 2

                  I went into Flash Pro CS5.5 and moved the registration point of a center reg screen to the top left of the view area. I then set a negative x and y to account for the black border. That lined up perfectly on the Android device. However, on the iPod 4 Touch (my only iOS device), the MovieClip is properly positioned but blown up four times larger than it should be. It appears that I still have the same problem... Things lay out properly on Android but iOS takes some messing around to make work.

                  • 6. Re: Layout on iOS differs from Layout on Android
                    alinator11 Level 2

                    I am still not understanding this. I changed the registration and ran on an Android device. My Android device and my iOS device both have screen sizes of 960 X 640. When I run on Android, I do not set any size constraints. I simply set my container to 100% of the width and height of the screen and add my MovieClip, move it by a negative x and y to remove the boarder and it lays out perfectly with no further adjustment.

                     

                    <s:SpriteVisualElement id="introContainer"

                                               x = "0"

                                               y="0"

                                               width="100%"

                                               height="100%"

                        />  

                     

                                    hholder = new MainHolderClip();

                                    hholder.x = -125;

                                    hholder.y = -70;

                                    introContainer.addChild(hholder);

                     

                    MainHolderClip, BTW, is a MovieClip I made to add the existing MovieClip to so that I could change the registration to upper left. In any event, that's all I do on Android and everything works fine...

                     

                    On the iOS device, I did the same and MainHolderClip shows up giant - you can only see 1/4 of the screen but it is positioned properly (with none of the black frame showing and in the upper left corner).  For some reason, it is huge. So, I took to setting the size of MainHolderClip if Capabilities finds that the OS is iOS. If iOS, I measure the screen by looking at the final size of introContainer. That reported as 480 X 320 which is exactly half of the size of the actual screen. I went ahead and used that measurement and as you'd expect, you get a perfectly centered, MovieClip that is half the size it should be. Therefore, I doubled the measurements and set MainHolderClip.width and height as 960 X 640. That gave me the same result as just letting Flex lay it out for me (which means that Flex presumably knows that the screen is really 960 X 640 and is setting MainHolderClip as such when I let it lay out by itself). Given all this, I started toying with the numbers until I dialed in an acceptable but not perfect result. This is all so tortured that it can't be right and will be impossible to manage in production. How do I fix this?

                     

                    On iOS to get it almost right on a 960 X 640 screen, I have to make the following declarations:

                     

                                    hholder.width = 635;

                                    hholder.height = 410;

                                    hholder.x = -70;

                                    hholder.y = -40;

                     

                     

                    This makes no sense to me. For instance, 635 wide is no logical number I can derive from the numbers I'm given. I have a MovieClip that is declared to be 1083 wide added to a screen that is measured at 960 in reality but reporting at 480 where if I set the MovieClip to the screen size, it's 4 times too large and if I set it to the reported size it's half the size it should be...

                     

                    Thanks for your help.

                    • 7. Re: Layout on iOS differs from Layout on Android
                      alinator11 Level 2

                      Any thoughts on this? Why is it laying out fine on Android but is too large on iOS? Is there something I can change about the FLA or on the Flex side to get a logical, predictable, working layout of my app?