Fill parent in Adobe Flex / MXML - apache-flex

I am attempting to implement a layout in Adobe Flex 4.5 / MXML where I have multiple controls in an HBox that - in total - consume all available horizontal screen estate. Some use a relative with (percentage), some an absolute width (pixels) and one is supposed to consume whatever space is still left, such as:
+----------------------+-----------------------+------+
| 35% | fill parent | 10px |
+----------------------+-----------------------+------+
How would I achieve this in Flex (is there something comparable to Android's layout_width="fillparent")?
Due to the fact that there are elements that have an absolute width I cannot easily calculate the width of the filler as a percentage a priori (as it varies with the screen width).
Setting the filler's width to 100% also does not work as this will shrink the 35% area below 35%.
Edit: Applying the updateDisplayList suggestion from www.Flextras.com's answer below yields the error "Call to a possibly undefined method updateDisplayList" at the line marked in the source excerpt below:
<?xml version="1.0"?>
<mx:HBox xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
creationPolicy="auto"
width="100%"
verticalGap="0">
[...]
<fx:Script>
<![CDATA[
[...]
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
super.updateDisplayList(unscaledWidth, unscaledHeight);
^^^^
headerLinkBox1.width = unscaledWidth * 0.375;
headerLinkBox2.width = unscaledWidth * 0.2;
headerLinkBox3.width = unscaledWidth - headerLinkBox4.width - headerLinkBox5.width - headerLinkBox6.width;
headerLinkBox1.x = 0;
headerLinkBox2.x = headerLinkBox1.x + headerLinkBox2.width;
headerLinkBox3.x = headerLinkBox2.x + headerLinkBox3.width;
headerLinkBox4.x = headerLinkBox3.x + headerLinkBox4.width;
headerLinkBox5.x = headerLinkBox4.x + headerLinkBox5.width;
headerLinkBox6.x = headerLinkBox5.x + headerLinkBox6.width;
}
[...]

You make use of the Flex Component lifecycle and write your own layout code in updateDisplayList. Conceptually something like this:
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void{
super.updateDisplayList(unscaledWidth, unscaledHeight);
// size the components
comp1.width = unscaledWidth*.35
comp2.width = unscaledWidth-10-comp1.width
comp3.width = 10;
// position the components
comp1.x = 0;
comp2.x = comp1.width;
comp3.x = comp2.x + comp2.width;
}
You'll probably want to set the height of each component to, possibly using the getExplicitOrMeasuredHeight() method.

Related

How can I show a line under each row of text in a Spark TextArea

I'm trying to show a horizontal line under each row of text in a Spark TextArea. I want to give the text area the look of legal paper.
Ah actually ran into a similar problem in Flex 3 doing a strikeout for a disabled link-button that was part of our styles. Just checked out the code and looked on the docs for the spark label and found the function I was using from a mx label explicitly says it won't work [from measureText() in spark label]:
Measures the specified text, assuming that it is displayed in a
single-line UITextField (or UIFTETextField) using a UITextFormat
determined by the styles of this UIComponent. Does not work for Spark
components since they don't use UITextField (or UIFTETextField). To
measure text in Spark components, get the measurements of a
spark.components.Label or spark.components.RichText
So I re-figured it out:
<?xml version="1.0" encoding="utf-8"?>
<s:Label xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark">
<fx:Script>
<![CDATA[
override protected function updateDisplayList(unscaledWidth : Number, unscaledHeight : Number) : void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
drawLines();
}
private function drawLines() : void
{
var totalHeight : Number = 0;
for (var i : int = 0; i < mx_internal::textLines.length; i++)
{
totalHeight = mx_internal::textLines[i].y;
graphics.lineStyle(1, 0x000000);
graphics.moveTo(0, totalHeight);
graphics.lineTo(width, totalHeight);
}
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
</s:Label>
Set the textDecoration style for the control.

slightly weird question about graphics in flex

Let's say you have a generic Graphics object with a fill and a a stroke. Is there an easy way to slice that object, for instance vertically, and get a Graphics object that represents a subset of it?
In other words, what would function slice below look like?
var gOriginal:Graphics;
var gNew:Graphics;
gNew = slice(gOriginal, "vertical", "0.5);
so that if gOriginal was an ellipse I now get half an ellipse?
thank you
f
I'm tagging onto Daniel's concept basically (except I have some deeply rooted, unexplained, disdain for degrafa), you should be able to do this:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="955"
minHeight="600"
creationComplete="application1_creationCompleteHandler(event)">
<s:layout>
<s:VerticalLayout/>
</s:layout>
<fx:Script>
<![CDATA[
import mx.core.UIComponent;
import mx.events.FlexEvent;
import spark.components.Group;
public var someGraphics:UIComponent;
[Bindable]
public var bd:BitmapData;
[Bindable]
public var finalBD:BitmapData;
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth,unscaledHeight);
if(someGraphics)
{
someGraphics.graphics.beginFill(0xFF0000);
someGraphics.graphics.drawEllipse(0,0,100,20);
someGraphics.graphics.endFill();
bd.draw(someGraphics);
finalBD.copyPixels(bd,new Rectangle(0,0,bd.width*.5,bd.height),new Point());
}
}
protected function application1_creationCompleteHandler(event:FlexEvent):void
{
someGraphics = new UIComponent();
bd = new BitmapData(100,100);
finalBD = new BitmapData(100,100);
invalidateDisplayList();
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:BitmapImage source="{bd}" width="100" height="100"/>
<s:BitmapImage source="{finalBD}" width="100" height="100"/>
</s:Application>
This is obviously far from a pretty solution but just doing the graphics into some object that has a graphics property, then using the BitmapData draw method to get the pixels into a bitmap data then copying out a section of those pixels to another BitmapData for display, this is probably more memory but less processor intensive than Jeremy's. Also I'm using UIComponent the requirement for the draw method is that the class implements IBitmapDrawable so whatever is the lightest implementation of that is probably what you want to use, UIComponent is a guess.
you could draw() it into a bitmap object and select a rectangle to include which could have the same effect, but there is no simple way of doing that.
you can also look into degrafa but that could become too complicated
There isn't a built in method for removing portions of a Graphics object. You will most likely need to treat each type of shape ( rect, ellipse, poly ) uniquely, in regard to "slicing" it. For instance to slice an elipse, similarly to taking a slice out of a pie chart, you would need to draw the shapes using a little trig to create a wedge.
Here is a basic function for drawing wedges:
protected function drawWedge():void
{
var g:Graphics = shape.graphics;
g.clear();
g.lineStyle( 1, 0xFFFFFF );
g.beginFill( 0xCCCCCC );
var radiusX:Number = 100;
var radiusY:Number = 100;
var angle:Number = 0;
var xpos:Number = 0;
var ypos:Number = 0;
var segments:Number = 25;
var degrees:Number = 45;
var inc:Number = degrees / ( segments );
for( var i:int = 0; i <= segments; i++ )
{
xpos = Math.cos( Math.PI * angle / 180 ) * radiusX;
ypos = Math.sin( Math.PI * angle / 180 ) * radiusY;
g.lineTo( xpos, ypos );
angle += inc;
}
g.endFill();
}
Adjusting the angleX and angleY vars when they are initialized will begin drawing the wedge at a different point along the circumference. If you change degrees to 360 you will see a complete circle, and at 180 you will get a half circle. Change radiusX and radiusY to create an oval. Also, segments controls the resolution of the drawing. Bump it up and you get a smoother curve.
Hope this helps.

Is there a multiline text workaround for Flex

Is there a workaround for displaying multiline text in Flex 3? The two controls I have tried so far are mx:Text, and mx:TextArea. Each control has its own bug associated with it. For reference: mx:Text bug - http://bugs.adobe.com/jira/browse/SDK-9819 mx:TextArea bug - http://bugs.adobe.com/jira/browse/SDK-12616. Basically, neither control handles scrolling correctly if you do not specify a height and the text wraps onto the next line (height is determined dynamically by Flex, based on the wrapping). Does anybody have a workaround that might be helpful?
Thanks.
Update: One of the methods I have tried in the past has been to manually calculate the height of a mx:Text element. I can do this by using the following:
var textItem:Text = new Text();
var len:int = value.length;
var lines:int = int(len/115) + 1;
var height:int = lines * 20;
textItem.height = height;
While this seems to get around the problem in mx:Text, there is one big fault. The calculation relies heavily on font-size, letter-spacing, and the width of textItem. I can use this method, and move on with my project. However, maintenance on this is inevitable, and with code like this, it will a gigantic PITA.
I've had to deal with this a few times myself. The best way I've found to get dynamic height sizing of <mx:Text> is to leave the height out of the text and then specify a percent height of 100% on the enclosing VBox, HBox, etc. Something like the following should work for you:
<mx:VBox width="100%" height="100%">
<mx:Text text="Your really long text goes here." width="100%"/>
</mx:VBox>
As this is a bit of a hack itself, your milage may vary.
Edit
If you want to extend your above example so that maintenance on the code is easier, you should look into the TextLineMetrics class. This will allow you to measure the width and height of your text, taking into account font, size, etc. The docs for TextLineMetrics can be found here. To use your above example, you'd want to do something like the following:
var textItem:Text = new Text();
var metrics:TextLineMetrics = textItem.measureText( value );
var len:int = metrics.width;
var lines:int = int(len/textItem.width) + 1;
var height:int = lines * metrics.height;
textItem.height = height;
I use a variable height text area class that works very well for me:
package
{
import mx.controls.TextArea;
/**
* TextArea that xpands to the height of the content contained
* within.
* #author joel
*
*/
public class VariableHeightTextArea extends TextArea
{
public function VariableHeightTextArea()
{
super();
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number) : void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
if(this.height != int(this.textField.measuredHeight) + 5 )
{
this.height = this.textField.measuredHeight + 5;
}
}
}
}

Fit Flex datagrid to data

I have rows of text data that can vary between 0 and 100, and all need to be visible on the screen at one time. The default behavior is fine for the grid until the rows * rowHeight > gridHeight.
Basically I need a hook into the item height, or row height to calculate it based on the height of the grid. I've set paddingTop and paddingBottom to zero, but there is still a considerable amount of white space in between rows.
My datagrid component...
<mx:DataGrid xmlns:mx="http://www.adobe.com/2006/mxml"
creationComplete="OnCreationComplete()"
paddingTop="0"
paddingBottom="0"
>
<mx:Script>
<![CDATA[
private function OnCreationComplete():void
{
//setRowHeight(10);
}
override protected function setRowHeight(v:Number):void
{
super.setRowHeight(v);
}
]]>
</mx:Script>
</mx:DataGrid>
setRowHeight() helps, but the itemRender for the cell bigger than the cell, if I set the row height to something like 10.
You might want to look at the DataGrid.variableRowHeight property as, when this is set to false (the default) all rows will be the same height as the largest itemRenderer. You could also look into writing your own itemRenderer for each DataColumn.
If all you really want to do is set the row height based on the number of items in the dataProvider though, you could just set the DataGrid.rowHeight property like this (assuming your grid has a fixed height, say 100%) :
myDataGrid.dataProvider = myArray;
myGrid.rowHeight = Math.floor((myGrid.height - myGrid.headerHeight)/myArray.length);
(I'm taking the floor here because if you end up with a fractional value that rounds up, you'll need a scroll bar)
The only problem with this approach, as I think you've noticed, is that the itemRenderer might not display properly in a row that's too small. I guess you could solve this by changing the font within the renderer based on its height.
Thank you inferis, that helped me a lot. This is my final grid component. It's not really self contained because of a few call-outs, but if it helps someone else get theirs to work, great!
<?xml version="1.0" encoding="utf-8"?>
<mx:DataGrid xmlns:mx="http://www.adobe.com/2006/mxml"
paddingTop="-3"
paddingBottom="-3"
resize="OnResize(event)"
>
<mx:Script>
<![CDATA[
import mx.containers.Panel;
import mx.core.Application;
import mx.events.ResizeEvent;
protected function OnResize(event:ResizeEvent):void
{
this.invalidateDisplayList();
}
/**
* #private
* Sizes and positions the column headers, columns, and items based on the
* size of the DataGrid.
*/
override protected function updateDisplayList(unscaledWidth:Number,
unscaledHeight:Number):void
{
if( this.collection.length > 0 ) // so don't divide by zero
{
var appHeight:Number = Application.application.height;
var appMinusMenuHeight:Number = appHeight - Application.application.hboxPrintBar.height;
var boxHeight:Number = Application.application.vboxViewAll.height;
if( boxHeight > 0 )
{
var gridHeight:Number = (appMinusMenuHeight - this.headerHeight) * 0.93;
var calcHeight:Number = gridHeight / this.collection.length;
var calcHeightFloor:Number = Math.floor( calcHeight );
setRowHeight( calcHeightFloor );
//var p:Panel = this.parent as Panel;
//p.title = calcHeightFloor.toString();
if( calcHeightFloor <= 10 )
this.setStyle("fontSize",calcHeightFloor);
}
}
super.updateDisplayList(unscaledWidth,unscaledHeight);
}
]]>
</mx:Script>
</mx:DataGrid>

How to calculate the size of a sprite?

I have a sprite that I do some custom drawing in, but I would like the container to know where to position the sprite properly. To do this, the container needs to know how big the sprite is. UIComponents go through a measure stage, but sprites don't . How do I calculate the size that a sprite will be?
Edit: I'm doing the drawing in Event.ENTER_FRAME, and it's animated, so I can't tell ahead of time how big it's going to be. The UIComponent has a measure function and I'd like to create something similar.
The precise answer, as far as I can gather is, you can't tell ahead of time, you must actually draw into the sprite to determine it's size.
Sprites take the size that you draw in them. It has no size at all until you've drawn something in it. If your application allows you can draw a border (rectangle maybe) first and then measure the sprite. But don't draw outside the borders later.
Also, depending on what you are drawing, then you may be able to use math to pre-calculate the final size.
i.e. if you are drawing a circle, you could use Math to figure out the final height / width.
mike
Have a look here -- I hope this answers your question:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Script>
<![CDATA[
import mx.core.UIComponent;
private var s:Sprite;
private var w:UIComponent;
override protected function createChildren():void
{
super.createChildren();
if (!s)
s = getSprite();
w = new UIComponent();
trace("The sprite measures " + s.width + " by " + s.height + ".");
w.addChild(s);
box.addChild(w);
}
private function getSprite():Sprite
{
var s:Sprite = new Sprite();
s.graphics.lineStyle(1, 0xFFFFFF);
s.graphics.beginFill(0xFFFFFF, 1);
s.graphics.drawRect(0, 0, Math.floor(Math.random() * 1000), Math.floor(Math.random() * 1000));
s.graphics.endFill();
return s;
}
]]>
</mx:Script>
<mx:Box id="box" backgroundColor="#FFFFFF" />
</mx:Application>
If you run this, the trace statement should display the height and width of the drawn Sprite, which is randomly generated. In other words, you can get the height and width of the sprite simply by querying its height and width properties.
If you need to layout some others components based on the Sprite width and height, but before actually drawing it, you can draw on a flash.display.Shape object, use this size as a reference, and then attach the Shape on the Sprite.

Resources