Skip navigation
Currently Being Moderated

Custom Flex component has wrong width/height

Feb 11, 2012 8:18 AM

Hi,

 

I'm working on a custom Flex component--a callout bubble with a Spark Label for text--and I'm having problems with its size. On screen, the component is 179 x 138 pixels. However, when I trace() the width/height of the component after it is created, I'm told it is 134 x 108. This is making it impossible to correctly position the callout based on its size!

 

As you can see, I'm overriding updateDisplayList() and using the Graphics API to draw the callout bubble with a gradient:

 

override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
    super.updateDisplayList(unscaledWidth, unscaledHeight);
    
    graphics.clear();
    
    label.move(15, 15);
    
    var matrix:Matrix = new Matrix();
    matrix.createGradientBox(unscaledWidth + 30, unscaledHeight + 30, Math.PI / 2, 0, unscaledHeight / 2);
    
    graphics.beginGradientFill(GradientType.LINEAR, [0x000000, 0x000000], [0.7, 0.7], [0, 255], matrix, SpreadMethod.PAD, InterpolationMethod.RGB);
    graphics.drawRoundRect(0, 0, unscaledWidth + 30, unscaledHeight + 30, 20, 20);
    
    var arrowWidth:Number  = 15;
    var arrowHeight:Number = 36;
    var arrowTop:Number    = (unscaledHeight + 30 - arrowHeight) / 2;
    
    graphics.moveTo(0, arrowTop);
    graphics.lineTo(-arrowWidth, arrowTop + arrowHeight / 2);
    graphics.lineTo(0, arrowTop + arrowHeight);
    graphics.endFill();
}

 

 

And, as you can see, when the text of the callout's label is changed, I'm invalidating properties and size:

 

public function set text(value:String):void
{
    _text = value;
    textChanged = true;
    label.text = text;
    label.addEventListener(FlexEvent.UPDATE_COMPLETE, labelReady);
}
 
private function labelReady(event:FlexEvent):void
{
    label.removeEventListener(FlexEvent.UPDATE_COMPLETE, labelReady);
    invalidateProperties();
    invalidateSize();
 }
 
override protected function commitProperties():void
{
    super.commitProperties();
    
    if (textChanged)
    {
        textChanged = false;
        invalidateDisplayList();
    }
}

 

Any ideas what I'm doing wrong?

 
Replies
  • Currently Being Moderated
    May 23, 2012 8:09 AM   in reply to Stenrap2

    Similar problem here. I am setting the component's width/height in the measure() method, but the component's height/width properties are always 0 when they are traced. Is this a bug?

     
    |
    Mark as:
  • Currently Being Moderated
    May 23, 2012 10:43 AM   in reply to Keith Lee

    It is not recommended to set width/height in measure().  See the documentation on custom components and how commitProperties/measure/updateDisplayList work.

     
    |
    Mark as:
  • Currently Being Moderated
    May 23, 2012 4:45 PM   in reply to Flex harUI

    Really? Based on everything I've read so far, the measure() method is where the component's default width/height is defined.

     

    I've determined that in order to read the component's width/height properties, I must do so after it's creationComplete event fires. Even though within the component itself, I can trace the component's measuredHeight & measuredWidth properties and they are valid immediatedly after the component is added to the DisplayList, the parent does not see them. Does that sound right?

     

    My component won't ever change size actually, so should I be setting it's maxWidth & minWidth properties too?

     

    thx

     
    |
    Mark as:
  • Currently Being Moderated
    May 23, 2012 5:12 PM   in reply to Keith Lee

    Yes, really.

     

    Measure() should only provide measuredWidth/Height and measuredMinWidth/Height.  Later, in updateDisplayList(), each parent calls setActualSize or setLayoutBoundsSize on its children to give them their sizes (so they can apply sizes to their children).   Only then is the width/height valid.

     

    Setting width/height any sooner locks in the size of the component prevent resizing if needed to shrink or grow to fit in.  It can also cause the parent to have to stop and re-measure which will be a  drag on performance.

     

    You only need to set maxWidth/Height if the layout is going to try to stretch them.

     

    -Alex

     
    |
    Mark as:
  • Currently Being Moderated
    May 25, 2012 9:20 AM   in reply to Flex harUI

    Hi,

     

    Maybe I mist-stated what I was doing... here is one of my custom components. Does it look correct?

     

    thx

     

    package components

    {

              import flash.display.Shape;

              import flash.geom.ColorTransform;

              import flash.text.TextField;

              import flash.text.TextFieldAutoSize;

              import flash.text.TextFormat;

              import flash.text.TextFormatAlign;

     

              import mx.core.UIComponent;

     

              import ui.LayoutConstants;

     

              public class Tab extends UIComponent

              {

                        private var _tabName:String;

                        private var _tabNameChanged:Boolean;

                        private var _labelTextfield:TextField;

                        private var _bkgrndShape:Shape;

                        private var _bkgrndColor:uint;

                        private var _bkgrndColorChanged:Boolean;

                        private var _tabType:String;

                        private var _textFormat:TextFormat;

     

                        public function Tab()

                        {

                                  super();

                                  _bkgrndColor = LayoutConstants.TAB_ACTIVE_COLOR;

                        }

     

     

                        override protected function createChildren():void

                        {

                                  super.createChildren();

     

                                  if (!_bkgrndShape)

                                  {

                                            _bkgrndShape = new Shape();

                                            _bkgrndShape.graphics.lineStyle(1, _bkgrndColor);

                                            _bkgrndShape.graphics.beginFill(_bkgrndColor);

                                            _bkgrndShape.graphics.moveTo(0, 0);

                                            _bkgrndShape.graphics.lineTo(0, -40);

                                            _bkgrndShape.graphics.lineTo(2, -42);

                                            _bkgrndShape.graphics.lineTo(4, -44);

                                            _bkgrndShape.graphics.lineTo(LayoutConstants.TAB_ WIDTH - 10, -44);

                                            _bkgrndShape.graphics.lineTo(LayoutConstants.TAB_ WIDTH, -34);

                                            _bkgrndShape.graphics.lineTo(LayoutConstants.TAB_ WIDTH, 0);

                                            _bkgrndShape.graphics.lineTo(0, 0);

                                            _bkgrndShape.graphics.endFill();

                                            addChild(_bkgrndShape);

                                  }

     

                                  if (!_labelTextfield)

                                  {

                                            _textFormat = new TextFormat();

     

                                            _textFormat.color = "0x000000";

                                            _textFormat.font = LayoutConstants.UI_BOLD_FONT;

                                            _textFormat.bold = true;

                                            _textFormat.size = 12;

                                            _textFormat.align = TextFormatAlign.CENTER;

     

                                            _labelTextfield = new TextField();

                                            _labelTextfield.selectable = false;

                                            _labelTextfield.mouseEnabled = false;

                                            _labelTextfield.wordWrap = true;

                                            addChild(_labelTextfield);

                                            _labelTextfield.autoSize = TextFieldAutoSize.CENTER;

                                  }

                        }

     

                        override protected function commitProperties():void

                        {

                                  super.commitProperties();

     

                                  if (_tabNameChanged)

                                  {

                                            _labelTextfield.text = _tabName;

                                            _labelTextfield.setTextFormat(_textFormat);

                                            _tabNameChanged = false;

                                  }

     

                                  if (_bkgrndColorChanged)

                                  {

                                            var colorTransform:ColorTransform = new ColorTransform();

     

                                            colorTransform.color = _bkgrndColor;

                                            _bkgrndShape.transform.colorTransform = colorTransform;

                                            _bkgrndColorChanged = false;

                                  }

                        }

     

                        override protected function measure():void

                        {

                                  super.measure();

     

                                  measuredWidth = _bkgrndShape.width;

                                  measuredHeight = _bkgrndShape.height;

                                  measuredMinHeight = _bkgrndShape.height;

                                  measuredMinWidth = _bkgrndShape.width;

                        }

     

                        override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void

                        {

                                  super.updateDisplayList(unscaledWidth, unscaledHeight);

     

     

                                  _labelTextfield.x = (unscaledWidth / 2) - (_labelTextfield.width / 2);

                                  _labelTextfield.y = -(unscaledHeight / 2) - (_labelTextfield.height / 2);

                        }

     

                        [Bindable]

                        public function get tabName():String

                        {

                                  return _tabName;

                        }

     

                        public function set tabName(value:String):void

                        {

                                  _tabName = value;

                                  _tabNameChanged = true;

                                  invalidateProperties();

                                  invalidateDisplayList();

                        }

     

     

                        [Bindable]

                        public function get tabType():String

                        {

                                  return _tabType;

                        }

     

                        public function set tabType(value:String):void

                        {

                                  _tabType = value;

                        }

     

                        public function set bkgrndColor(value:uint):void

                        {

                                  _bkgrndColor = value;

                                  _bkgrndColorChanged = true;

                                  invalidateProperties();

                                  //invalidateDisplayList();

                        }

              }

    }

     
    |
    Mark as:
  • Currently Being Moderated
    May 25, 2012 11:08 AM   in reply to Keith Lee

    Seems like your measure() method isn’t taking into account the textfield and the background shape?

     
    |
    Mark as:
  • Currently Being Moderated
    May 25, 2012 12:05 PM   in reply to Flex harUI

    Hmm... not sure I follow you. The textfield sits on top of the background image, so the measure() method just sets the size of the component to the size of the shape.

     
    |
    Mark as:
  • Currently Being Moderated
    May 25, 2012 11:19 PM   in reply to Keith Lee

    Maybe I don’t understand what you are looking for.  You are apparently not the original poster?

     

    I also noticed you seem to be drawing the background shape into negative coordinates.

     

    -Alex

     
    |
    Mark as:
  • Currently Being Moderated
    May 29, 2012 12:07 PM   in reply to Flex harUI

    Hi,

     

    I guess the question is, at what point after a custom componnent has been added to the display list using the addElement method can it's width & height properties be accessed and valid values be returned? Immediately querying the component's width and height does not work.

     

    thx

     
    |
    Mark as:
  • Currently Being Moderated
    May 29, 2012 1:25 PM   in reply to Keith Lee

    Unless the child has been given an explicit width and height by having those properties set directly, because parents size and position their children a child’s width/height is not known until the parent component has completed its updateDisplayList call.  There is no event for that, but an updateComplete event should follow shortly and can be forced by calling validateNow on the parent or one of its parents that have enough information to determine the width and height.

     
    |
    Mark as:

More Like This

  • Retrieving data ...

Bookmarked By (0)

Answers + Points = Status

  • 10 points awarded for Correct Answers
  • 5 points awarded for Helpful Answers
  • 10,000+ points
  • 1,001-10,000 points
  • 501-1,000 points
  • 5-500 points