8 Replies Latest reply on May 9, 2009 8:50 AM by emurmur

    Button rollover causing everything to redraw

    emurmur Level 1

      I am having a problem with my Flex application.  It seems that rolling over a button causes my custom component to redraw.  This causes all button rollovers to be very sluggish.  This can be seen in the attached graphic - it shows the redraw boxes that occur when the mouse rolls over the button named 'foo'.

       

      RedrawArea.PNG

       


      So my question is, why would rolling over that button cause everything else to draw?  They do not overlap at all.  How can I limit the redraw?

       

      I've copied the code to a simplified version of the application that shows the issue.

       

      <?xml version="1.0" encoding="utf-8"?>
      <mx:Application
          xmlns:mx="http://www.adobe.com/2006/mxml"
          xmlns:cc="components.*"
          layout="horizontal"
          applicationComplete="stage.invalidate()">
         
          <mx:VBox
              width="100">
              <mx:Button
                  id="fooButton"
                  label="foo"
                  click="stage.invalidate()"
                  />
          </mx:VBox>
         
          <mx:Spacer width="5" />
          <cc:TestComponent
              id="testCanvas"
              width="100%"
              height="100%"
              />
      </mx:Application>

       

      Here is a simplified component that shows the issue:


      package components
      {
          import flash.events.Event;
         
          import mx.core.UIComponent;

       

          public class TestComponent extends UIComponent
          {
              public function TestComponent()
              {
                  super();
                 
                  addEventListener(Event.RENDER, onRender);
              }
             
              private function onRender(theEvent:Event):void
              {
                  this.graphics.lineStyle(1.0, 0x000000);
                  graphics.beginFill(0xFEFEFE);
                  graphics.drawEllipse(1,1,width-2,height-2);
                  graphics.endFill();
              }
          }
      }

        • 1. Re: Button rollover causing everything to redraw
          Gregory Lafrance Level 6

          If an auto-layout container is involved, rollovers probably fire off measuring sizes in the auto-layout container.

           

          So use a Canvas, or else you can turn the autoLayout property of the container to off, as long as it will not cause other bad side-effects, but that depends on your UI design.

          • 2. Re: Button rollover causing everything to redraw
            emurmur Level 1

            Greg, thanks for your reply.  I updated the sample code with your suggestions, but no luck. The only two components in the sample that have autoLayout properties are the Application and the VBox.  I turned autolayout off for both of them, but I still get the same result; rolling over the button causes both the button and the custome UIComponent to redraw.  So I don't think it is an auto-layout issue.  Any other ideas about why rolling over a button would cause all the other elements in the interface to redraw?

             

            Thanks again for your reply.

             

            Here is the updated code:

             

            <?xml version="1.0" encoding="utf-8"?>
            <mx:Application
                xmlns:mx="http://www.adobe.com/2006/mxml"
                xmlns:cc="components.*"
                layout="horizontal"
                applicationComplete="stage.invalidate()"
                autoLayout="false">
               
               <mx:VBox
                    width="100"
                    autoLayout="false">
                    <mx:Button
                        id="fooButton"
                        label="foo"
                        click="stage.invalidate()"
                        />
                </mx:VBox>
               
                <mx:Spacer width="5" />
                <cc:TestComponent
                    id="testCanvas"
                    width="100%"
                    height="100%"
                    />
            </mx:Application>

            • 3. Re: Button rollover causing everything to redraw
              Flex harUI Adobe Employee

              What happens when you take out the calls to stage.invalidate().

               

              Alex Harui

              Flex SDK Developer

              Adobe Systems Inc.

              Blog: http://blogs.adobe.com/aharui

              • 4. Re: Button rollover causing everything to redraw
                emurmur Level 1

                If I take out the calls to stage.invalidate, I get the same behavior, except that the custom component is not initially drawn.  It does not draw until rolling over the button.  That is why I had the applicationComplete="stage.invalidate()" in the Application class - so the custom component would be drawing initially.  That itself seems strange.

                 

                So, both the button and the custom component are redrawn if I roll over the button.

                 

                Here is the modified code:

                 

                <?xml version="1.0" encoding="utf-8"?>
                <mx:Application
                    xmlns:mx="http://www.adobe.com/2006/mxml"
                    xmlns:cc="components.*"
                    layout="horizontal"
                    autoLayout="false">
                   
                    <mx:VBox
                        width="100"
                        autoLayout="false">
                        <mx:Button
                            id="fooButton"
                            label="foo"
                            />
                    </mx:VBox>
                   
                    <mx:Spacer width="5" />
                    <cc:TestComponent
                        id="testCanvas"
                        width="100%"
                        height="100%"
                        />
                </mx:Application>

                • 5. Re: Button rollover causing everything to redraw
                  Flex harUI Adobe Employee

                  There is a well-defined component lifecycle that all custom components should follow in order to provide for efficiency in the application.  Please use the lifecycle methods (createChildren, commitProperties, measure, updateDisplayList) otherwise you can have issues like this.  There is plenty of documentation on creating custom components.

                   

                  For sure, your component is going to keep aggregating graphics since it never calls graphics.clear() and eventually the display list for that component will be huge, but beyond that, because you are not implementing the measure() method, the component is probably not reporting a legitimate size.

                   

                  Alex Harui

                  Flex SDK Developer

                  Adobe Systems Inc.

                  Blog: http://blogs.adobe.com/aharui

                  1 person found this helpful
                  • 6. Re: Button rollover causing everything to redraw
                    emurmur Level 1

                    Alex, thanks.  That is very helpful.  I'm new to Flex.  I naively thought that graphics.clear() was a simple erase.  I'll go back and learn the lifecycle stuff, modify the test application and post the results.  Thanks.

                    • 7. Re: Button rollover causing everything to redraw
                      Gregory Lafrance Level 6

                      This LiveDocs page gives a good description of some important aspects of creating components, and a description of the component life cycle:

                       

                      http://livedocs.adobe.com/flex/3/html/help.html?content=ascomponents_advanced_2.html

                       

                      If this post helps, please mark it as such.

                      • 8. Re: Button rollover causing everything to redraw
                        emurmur Level 1

                        Thank you Alex, that solved my problem.  I got rid of the RENDER handler and implemented updateDisplayList().  This fixed 3 problems I was having.  First, it fixed the redraw issue.  Now when I roll over the button, only the button is updated.  Second, I did have a serious performance degradation problem that occured over time.  This was because I was not calling Graphics.clear() before I rendered the custom component's rectangle.  Apparently it was building up many copies of the rectangle.  Third, I can now get rid of the stage.invalidate() on applicationComplete because the custom component draws correctly at startup.

                         

                        My underlying problem was I was treating Flex/Flash like other graphic systems I have used before.  I thought I was actually drawing on the RENDER message.  Now I know that when I make Graphics calls, I'm really specifying vector shapes that will be used by the framework to do the drawing.  Graphics.clear() doesn't erase the background, it clears out the display list (the vector shape list). I don't need to handle the RENDER message, I just need to implement updateDisplayList() and do my drawing there.

                         

                        The Flex documentation is really good.  I found everything that I need in Chapter 9 of Creating and Extending Adobe Flex 3 Components.  For those reading this, here is the url for the documentation downloads on Adobe's site:

                         

                        http://www.adobe.com/support/documentation/en/flex/

                         

                        Here is the updated code that works the way it should.  Thanks.

                         

                        <?xml version="1.0" encoding="utf-8"?>
                        <mx:Application
                            xmlns:mx="http://www.adobe.com/2006/mxml"
                            xmlns:cc="components.*"
                            layout="horizontal"
                            autoLayout="false">
                           
                            <mx:VBox
                                width="100"
                                autoLayout="false">
                                <mx:Button
                                    id="fooButton"
                                    label="foo"
                                    />
                            </mx:VBox>
                           
                            <mx:Spacer width="5" />
                            <cc:TestComponent
                                id="testCanvas"
                                width="100%"
                                height="100%"
                                />
                        </mx:Application>

                         

                        Here is the code for the custom component:

                         

                        public class TestComponent extends UIComponent
                            {
                                public function TestComponent()
                                {
                                    super();
                                }
                               
                                //
                                // draw the components image
                                //
                                override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
                                {
                                    //
                                    // use inherited logic to set width, height based on
                                    // what the framework has passed us.
                                    //
                                    super.updateDisplayList(unscaledWidth, unscaledHeight);

                         

                                    //
                                    // draw the component's image
                                    //
                                    this.graphics.lineStyle(1.0, 0x000000);
                                    graphics.beginFill(0xFEFEFE);
                                    graphics.drawEllipse(1,1,width-2,height-2);
                                    graphics.endFill();
                                }
                               
                                //
                                // tell the framework the default size for this component
                                //
                                override protected function measure():void
                                {
                                    super.measure();
                                   
                                    // these are fairly arbitrary because this
                                    // component has no children or internal detail
                                    this.measuredMinHeight = 100;
                                    this.measuredHeight = 400;
                                    this.measuredMinWidth = 100;
                                    this.measuredWidth = 400;
                                }
                            }
                        }