Absolute positioning in Flex? - apache-flex

I need to programmatically add a set of controls with some amount of pixels between them. I can't seem to find how to do this in the Flex docs. How can I do it?

Most Containers have some logic to place the items for you, e.g. vertically or horizontally. I.e. if you want to place them horizontally with 5 pixels of space you would use a HBox (VBox for vertical layout):
<mx:HBox horizontalGap="5">
<Component1/>
<Component2/>
<etc.../>
</mx:HBox>
Or script:
...
var box: HBox = new HBox();
box.horizontalGap = 5;
box.addChild(new Component1());
box.addChild(new Component2());
addChild(box);
But if you want to place them yourself using x,y coordinates (i.e. absolute positioning) you can use Canvas:
<mx:Canvas>
<Component1 x="100" y="100"/>
<Component2 x="100" y="200"/>
<etc.../>
</mx:Canvas>
script version:
var canvas: Canvas = new Canvas();
var component1: Component1 = new Component1();
component1.x = 100;
component1.y = 100;
canvas.addChild(component1);
var component2: Component2 = new Component2();
component2.x = 100;
component2.y = 100;
canvas.addChild(component2);

Inside a container with absolute positioning, e.g. Canvas, you can position elements with x and y (or right, left, top, bottom)
elem.x = 100;
elem.y = 200;
canvas.addChild(elem);

You can also use Spacer to add some space in between components.
<mx:HBox>
<Component1 />
<mx:Spacer width="10" />
<Component2 />
</mx:HBox>

If your window can be re-sized, absolute layout is not recommended - It may be a good idea to use width="100%" and height=100% and then use the minHeight/minWidth/maxWidth etc.
In your case, you could set the minimum width/height of the spacer (between 2 components) so that the page scales proportionately.

Related

Extra Pixels In Button Width

<s:Button id="btn1" label="1" width="18" includeInLayout="true" visible="true" click="onSmallBtnClick();" />
<s:Button id ="btn2" label="2" width="18" includeInLayout="true" visible="true" click="onSmallBtnClick();" />
<s:Button id ="btn3" label="3" width="18" includeInLayout="true" visible="true" click="onSmallBtnClick();" />
<s:Button id="bigButton" label="bigButton" width="72" includeInLayout="false" visible="false" />
private function onSmallBtnClick():void {
hideBtn(btn1);
hideBtn(btn2);
hideBtn(btn3);
showBtn(bigButton);
}
private function showBtn(button:Button):void {
button.visible = true;
button.includeInLayout = true;
}
private function hideBtn(button:Button):void {
button.visible = false;
button.includeInLayout = false;
}
Hi all, in the Flex code above, I have 4 four buttons on my interface. The interface should begin with small buttons 1,2,3 visible and the bigButton invisible. When either of the small buttons are clicked, the bigButton appears in place of the 3 small buttons. The widths of the small buttons are set at 18, while the bigButton width is set at 72.
My question is, shouldn't the width of the bigButton be 54, as 18x3 = 54? Or is there some padding within the buttons that I should know of? In this live docs page, it says..
By default, Flex stretches the Button control width to fit the size of
its label, any icon, plus 6 pixels of padding around the icon. You can
override this default width by explicitly setting the width property
of the Button control to a specific value or to a percentage of its
parent container. If you specify a percentage value, the button
resizes between its minimum and maximum widths as the size of its
parent container changes.
Could this be the problem? Since I have 3 buttons and the padding around the icons take up 6x3 = 18 pixels. The buttons don't seem to have any gap between them. So I am curious to know where does the extra 18 pixels come from.
Thanks.
As soon as I don't see any x or left things set for buttons, I guess you use something like HGroup or any other container with HorizontalLayout.
It has a property named gap, and this property is 6 by default.
But it will not add 18px, it will add 12px...
Try to set minWidth to 0 for all buttons.

Flex Vgroup Absolute positioning of components

I have a Vgroup with some components aligned par with its layout properties(vertical). I need to add one more component at an absolute X,Y position, overriding the alignment. I tried includeinlayout=false, but the component turns invisible then. Is it possible in flex?
No, this is not possible. A VGroup will ignore properties such as X, and Y. IF the component is visible, then includeInLayout is also ignored.
You'll have to layout your extra component outside of the VGroup, or switch to a Group and layout everything absolutely.
It is not possible! But you always can get global coordinates of the needed DisplayObject and show some PopUps or other components near to this target.
MXML:
<s:VGroup x="50" y="50">
<s:Button width="250" height="250" id="b1"/>
<s:Button width="250" height="250" id="b2"/>
</s:VGroup>
<s:Button id="addon"/>
AS:
var rect:Rectangle = b2.getBounds(this);
addon.x = rect.x + rect.width - addon.width;
addon.y = rect.y;

how to create up/down movement of sprite with as3 with a mouse

I need to move a sprite only vertically on mouse move. How do I implement it with as3?
Thanks
Flash version
var s:Sprite = new Sprite();
s.x = 20;
s.graphics.beginFill(0xFF0000);
s.graphics.drawRect(0,0,20,20);
addChild(s);
stage.addEventListener(MouseEvent.MOUSE_MOVE,moveSprite);
function moveSprite(e:MouseEvent):void
{
s.y = e.localY;
}
Flex version
<mx:Canvas width="100" height="100">
<mx:mouseMove>
<![CDATA[
s.y = event.localY;
]]>
</mx:mouseMove>
<mx:Canvas id="s" backgroundColor="#ff0000" width="20" height="20"/>
</mx:Canvas>
Each of these you can paste in and will do what you said. it will create a 20x20 red box that is vertically the same as the mouse but fixed horizontally. The flex version your mouse has to be within the containing Canvas.
addEventListener(MouseEvent.CLICK, clickHandler);
function clickHandler(e:MouseEvent):void{
mySprite.y += amount;
}
Ok, dragging is a little more complicated. You need to define a rectangle for the bounds of the dragging. If you want to just drag along one axis then you make the rectangle have a width of 0. In this example I've restricted the amount of scrolling and and down to different numbers that you can change below.
import flash.events.MouseEvent;
import flash.geom.Rectangle;
mySprite.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
function mouseDownHandler(event:MouseEvent):void{
stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
var scrollUpAmount:int = 10;
var scrollDownAmount:int = 200;
var boundsRect:Rectangle = new Rectangle(mySprite.x,mySprite.y-scrollUpAmount,0,mySprite.y+scrollDownAmount);
mySprite.startDrag(false, boundsRect);
}
function mouseUpHandler(event:MouseEvent):void{
stage.removeEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
mySprite.stopDrag();
}

Flex collision testing with hitTestObject

I'm trying to test clipping on two canvases. Both canvases are 100px wide. They're 20px apart. I've placed a label inside one and made it 200px wide. Scroll bars will show up on the canvas. When I don't have the label inside and use hitTestObject it returns false. When I place the label inside it returns true. Is there any way to alter the canvas with the label inside so that it doesn't expand to the width of the label?
<?xml version="1.0" encoding="utf-8"?>
private function init() : void {
var hitBox:Sprite = new Sprite;
hitBox.graphics.drawRect(box1.x, box1.y, 100, 100);
box1.hitArea = hitBox;
box1.mouseEnabled = false;
trace('box hit area: ' + box1.getBounds(box1));
trace('hitbox: ' + hitBox);
trace('box hit test: ' + box1.hitTestObject(box2));
}
]]>
</mx:Script>
<mx:Canvas id="box1" x="10" y="10" width="100" height="100" backgroundColor="#FFFFFF">
<mx:Label text="This is a test" width="200" />
</mx:Canvas>
<mx:Canvas id="box2" x="120" y="10" width="100" height="100" backgroundColor="#FFFFFF" />
Unfortunately, it doesn't look like you can accomplish what you want with hitTestObject. I played around with setting the clipContent and horizontalScrollPolicy properties on your canvases, to no use. What I think is happening is that hitTestObject considers your canvas to be as wide as the longest child component, regardless of any clip masks or scroll bars.
Are you forced to use hitTestObject? If not, I'd suggest writing your own collision detection function along the lines of:
public static function componentsCollide( obj1:DisplayObject, obj2:DisplayObject ):Boolean {
var bounds1:Rectangle = new Rectangle( obj1.x, obj1.y, obj1.width, obj1.height );
var bounds2:Rectangle = new Rectangle( obj2.x, obj2.y, obj2.width, obj2.height );
return bounds1.intersects( bounds2 );
}

How to make the Canvas clip its contents in Flex?

I draw a line on a Canvas object with the moveTo and lineTo graphics methods. If one end of the line lies outside the Canvas, the line spills out and is drawn over or under other elements in the application.
How do I make the Canvas keep the line contained within itself?
Thanks!
<mx:Canvas id="canvas" top="0" right="51" left="0" bottom="32">
<mx:Canvas x="-1" y="0" width="0" height="0"> <!-- This is a HACK to make the canvas clip -->
</mx:Canvas>
</mx:Canvas>
I had a similar problem some time ago. You need to embed another container inside the canvas, and draw the primitive graphics in that instead. I believe this is because the Canvas component only clips child components, and not primitive graphics.
Example here: http://www.adobe.com/cfusion/webforums/forum/messageview.cfm?forumid=60&catid=585&threadid=1421196. It includes some sample code about half way down the page.
The link in the recommended answer is broken. I solved the problem by placing another canvas inside my canvas that is larger than the outer canvas.
Example:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" applicationComplete="onComplete()">
<mx:Script><![CDATA[
private function onComplete():void
{
canvas.graphics.lineStyle(1);
canvas.graphics.moveTo( -100, -100);
canvas.graphics.lineTo(400, 400);
}
]]></mx:Script>
<mx:Canvas id="window"
height="300"
width="300"
clipContent="true"
horizontalScrollPolicy="off"
verticalScrollPolicy="off"
backgroundColor="white">
<mx:Canvas id="canvas" width="301" height="301">
</mx:Canvas>
</mx:Canvas>
</mx:Application>
If the window Canvas is going to be resized at runtime, add a resize event listener to resize the canvas Canvas also.
I have just developed a Flex Box component, which acts as a regular component container, but draws a rounded rectangle background, with another rounded rectangle indicated a fill-level. For that I needed to clip the upper section that should not get filled. Drawing the fill rectangle to the fill height was no option since the rounded corners would not match.
What I learned:
I created a Canvas component just for drawing the fill-level with bounds 0/0 and width/height of the Box
I added that canvas to the Box at index 0 via addChildAt()
I set the includeInLayout property to false for that canvas since it should not take part in the layouting of the Box itself but rather act as some floating drawing pane on top
I then added another Canvas as the mask to that fill-canvas (addChild(), and set mask property)
Here is some code:
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
// super
super.updateDisplayList(unscaledWidth, unscaledHeight);
// prep
var g:Graphics = this.graphics;
var fgColor:int = this.getStyle("fillColor");
var bgColor:int = this.getStyle("backgroundFillColor");
var radius:int = this.getStyle("cornerRadius");
// clear
g.clear();
// draw background
g.beginFill(bgColor, 1);
g.drawRoundRect(0, 0, unscaledWidth, unscaledHeight, radius, radius);
g.endFill();
// draw fill level
if (this._fillLevel > 0) {
var fillHeight:int = int(unscaledHeight * this._fillLevel);
// extra component for drawing fill-level, so we can apply mask just to this
if (this._fillLevelCanvas == null) {
this._fillLevelCanvas = new Canvas();
this._fillLevelCanvas.x = 0;
this._fillLevelCanvas.y = 0;
this._fillLevelCanvas.includeInLayout = false;
this.addChildAt(this._fillLevelCanvas, 0);
}
this._fillLevelCanvas.width = unscaledWidth;
this._fillLevelCanvas.height = unscaledHeight;
// mask
if (this._fillLevelMask == null) {
this._fillLevelMask = new Canvas();
this._fillLevelMask.x = 0;
this._fillLevelMask.y = 0;
this._fillLevelCanvas.addChild(this._fillLevelMask);
this._fillLevelCanvas.mask = this._fillLevelMask;
}
this._fillLevelMask.width = this.width;
this._fillLevelMask.height = this.height;
this._fillLevelMask.graphics.beginFill(0xFFFFFF);
this._fillLevelMask.graphics.drawRect(0, this.height-fillHeight, this._fillLevelMask.width, this._fillLevelMask.height);
this._fillLevelMask.graphics.endFill();
this._fillLevelCanvas.graphics.beginFill(fgColor, 1);
this._fillLevelCanvas.graphics.drawRoundRect(0, 0, unscaledWidth, unscaledHeight, radius, radius);
this._fillLevelCanvas.graphics.endFill();
}
}
Looks like this might be useful:
http://forums.adobe.com/message/199071#199071
Set ClipToBounds property of the Canvas to true:
<Canvas ClipToBounds="True">... Content ...</Canvas>

Resources