How to manage positions of DisplayObjects in actionscript? - apache-flex

I use the following code to set position of a DisplayObject to center.
obj.x = (screen_width - obj.width) / 2;
or
obj.x = (parentObj.width - obj.width) / 2;
And I use the following code to show 2 objects vertically.
obj2.x = obj1.width + interval
And I use the following code to show 2 objects vertically and set the position of them to center.
var obj3:Sprite = new Sprite;
obj3.addChild(obj1);
obj3.addChild(obj2);
obj2.x = obj1.width + interval;
obj3.x (screen_width - obj3.width) / 2;
Are the above codes a good way to manage positions of DisplayObjects?
Are there better and simple ways to do that?

Well you can also add them into a HGroup or a VGroup, (or HBox and VBox if you are using Flex 3). But when I want to implement a vertical layout without using these kind of elaborate objects, I usually create a "update()" method (note that the following code may contain syntax errors : it is just provided as an algorithm example):
private function update():void
{
// The vertical gap between elements
var gap:int = 4;
// The cumuled height
var cumuledHeight:Number = 0;
// The number of elements
var n:int = numElements;
// The element at each loop
var o:IVisualElement;
for (var i:int = 0; i<n; i++) {
// Gets the element
o = getElementAt(i);
// Sets its vertical position
o.y = cumuledHeight + gap*i;
// Updates the cumuled height
cumuledHeight += o.height;
}
}

Yes, there is a simpler way by using Flex to center it with horizontalCenter=0 and verticalCenter=0.

Related

Create a chess board in Flex 4.6

I'm starting a new project in Flash Builder 4.6 and need some advices before going in one direction instead of the right one.
Basically, my application in Flash Builder is for mobile devices and tablets, so I need to know how could I create a chess board that fix exactly the width of any device. I do not know how could i do it exactly in Flex.
Could anyone give an idea or supply an example i could use?
Thanks.
Why not use a TileGroup/TileLayout with 100% height and width? You then set the rowHeight to be {this.height / ROWS} (ROWS being however many rows there are on a Chessboard) and doing the same for columnWidth. You could even use the horizontalGap and verticalGap properties to show through to a black background that could be used as separators between each box (though you would have to account for this in your rowHeight and columnWidth calculations, obviously)
I would populate the screen via AS3, as opposed to Flex. Something like this is what I have in mind.
var columnCount:int = 0;
var rowCount:int = 0;
var boxes:int = 64; //however many boxes on a chess board goes here
for ( var i:Number = 0; i < boxes; i+ ){
var rect:Rect = new Rect();
var color:SolidColor;
rect.percentHeight = 100;
rect.percentWidth = 100;
rect.name = i.toString(); //this will make it easier to keep track of which box is which later on
if ( rowCount % 2 == 1 ) {
if ( i % 2 == 1 ) color = 0x000000;
else color = 0xffffff;
}
else {
if ( i % 2 == 1 ) color = 0xffffff;
else color = 0x000000;
}
this.rect.fill = color;
this.tileGroup.addElement(rect);
if ( columnCount == 7 ) {
columnCount = 0;
rowCount++;
}
else {
columnCount++;
}
}
That is completely off the top of my head and untested, so I am sure there is an error or two, but you get the idea of how it would work.
EDIT: As an afterthought, you could probably do the colors 100% mathematically without using rowCount/columnCount vars, though it is probably much much easier to do so.

flex: Drag and drop- object centering

In a drag+drop situation using Flex, I am trying to get the object center aligned to the point of drop- somehow, irrespective of the adjustments to height and width, it is always positioning drop point to left top.
here is the code..
imageX = SkinnableContainer(event.currentTarget).mouseX;
imageY = SkinnableContainer(event.currentTarget).mouseY;
// Error checks if imageX/imageY dont satisfy certain conditions- move to a default position
// img.width and img.height are both defined and traced to be 10- idea to center image to drop point
Image(event.dragInitiator).x = imageX-(img.width)/2;
Image(event.dragInitiator).y = imageY-(img.height)/2
The last 2 lines don't seem to have any effect. Any ideas why-must be something straightforward, that I am missing...
You can use the following snippet:
private function on_drag_start(event:MouseEvent):void
{
var drag_source:DragSource = new DragSource();
var drag_initiator:UIComponent = event.currentTarget as UIComponent;
var thumbnail:Image = new Image();
// Thumbnail initialization code goes here
var offset:Point = this.localToGlobal(new Point(0, 0));
offset.x -= event.stageX;
offset.y -= event.stageY;
DragManager.doDrag(drag_initiator, drag_source, event, thumbnail, offset.x + thumbnail.width / 2, offset.y + thumbnail.height / 2, 1.0);
}
Here is one important detail. The snippet uses stage coordinate system.
If you use event.localX and event.localY, this approach will fail in some cases. For example, you click-and-drag a movie clip. If you use localX and localY instead of stage coordinates, localX and localY will define coordinates in currently clicked part of the movie clip, not in the whole movie clip.
Use the xOffset and yOffset properties in the doDrag method of DragManager.
Look here for an example.

dynamic add component

I want to dynamically add component in Container like Canvas(TileList constraints each child has the same size, GridList is poor in performance), for example
<mx:Canvas id="myHolder" width="600" height="550">
</mx:Canvas>
<mx:Button label="Add Button" click="addButton()"/>
when I click the button, I hope add a component(whatever the component is, and maybe each component has different size), and if the total width of all added child is greater than myHolder, I hope the new child can begin in new line, and stretch the height of myHolder at the same time.(layout with custom code is better)
On Canvas you have complete freedom to lay components anywhere using their x and y properties, so there's a lot of ways to skin this cat. Since you need rows, one of the methods may be (not tested):
//inside of your Canvas-based component
private function updateChildrenPositions():void
{
var rowY:Number = 0;
var rowWidth:Number = 0;
var rowHeight:Number = 0;
for (var i:int = 0, total:int = numChildren; i < total; i++)
{
var child:DisplayObject = getChildAt(i);
if (rowWidth + child.width > width)
{
//child will cause overflow, start next row
rowY += rowHeight;
rowWidth = 0;
rowHeight = 0;
}
rowWidth += child.width;
child.x = rowWidth;
child.y = rowY;
if (child.height > rowHeight) rowHeight = child.height; //accumulating max height
}
height = rowY + rowHeight;
}
This assumes Canvas has fixed width and set height depending on layout. You can add paddings and gaps later, it's a good exercise :)
To get the functionality you want, I wouldn't use an HBox. As alxx suggested, a TileList would be a better fit in this situation.
Here are some examples using a TileList to get you started:
http://blog.flexexamples.com/category/halo/tilelist/
http://learn.adobe.com/wiki/display/Flex/TileList

why is this layout class not always working?

This is my attempt to write my own layout class for a panel of buttons (which may have between 2 and 20 buttons). Basically they should all be of a uniform size, with a constant spacing (5px) and resize appropriately.
However it doesn't always work.
Sometimes it works absolutely fine, but others it gives space for an extra column, or becomes unable to add additional columns on resizing (removing columns is fine), or something wont work. And it takes ages and seems horribly expensive in terms of computations. Reducing width seems significantly more painful in this respect for some reason.
Anyway, here it is:
package layouts
{
import mx.core.ILayoutElement;
import spark.components.supportClasses.GroupBase;
import spark.layouts.supportClasses.LayoutBase;
public class QButtonsLayout extends LayoutBase
{
public function QButtonsLayout()
{
super();
}
override public function measure():void
{
super.measure();
target.measuredHeight = 130;
}
override public function updateDisplayList(w:Number, h:Number):void
{
super.updateDisplayList(w,h);
var tCount:int = target.numElements; // Number of elements
var tW:Number = target.width; // Width of target (button area) - somewhere between 550 and 1000px
var maxW:Number = 1; // Largest natural width of any given element
var maxH:Number = 1; // Largest natural height of any given element
var eSetW:Number = 1; // Set (to be) width of each element upon the target
var eSetH:Number = 1; // Set (to be) height of each element upon the target
var tCols:Number = 1; // Number of columns upon the target
var tRows:Number = 1; // Number of rows upon the target
for (var i:int = 0; i<tCount; i++) // Find maxW
{
var layoutElement:ILayoutElement = useVirtualLayout ? target.getVirtualElementAt(i):target.getElementAt(i);
var thisW:Number = layoutElement.getPreferredBoundsWidth();
var thisH:Number = layoutElement.getPreferredBoundsHeight();
if(thisW > maxW)
{
maxW = thisW;
};
if(thisH > maxH)
{
maxH = thisH;
};
}
tCols = Math.floor((tW-5)/(maxW+5)); //Find maximum number of columns one can fit onto the target
if(tCols>tCount) //Fix to deal with cases with low tCounts
{
tCols = tCount;
};
tRows = Math.ceil(tCount/tCols); //Find corresponding number of rows
eSetW = ((tW-5)/tCols)-5; //Set widths of elements based upon number of columns, 5s to add some space between elements
eSetH = maxH; //Takes height as the largest height
for (var j:int = 0; j<tCount; j++)
{
var layoutElement2:ILayoutElement = useVirtualLayout ? target.getVirtualElementAt(j):target.getElementAt(j);
var eRow:int = Math.floor(j/tRows); //Row of given element, taking the 1st to be zero
var eCol:int = j - eRow*tRows; // Column of given element, again taking the 1st column as zero
var _x:Number = 5 + eRow*(eSetW+5);
var _y:Number = 5 + eCol*(eSetH+5);
layoutElement2.setLayoutBoundsPosition(_x,_y);
layoutElement2.setLayoutBoundsSize(eSetW,eSetH);
}
}
}
}
Any thoughts would be much appreciated.
Criticism more than welcome.
Turns out that it's not. The layout class itself is fine, as far as calculating element positions and size is concerned.
It is actually a problem in the way in which the buttons used calculated their prefered widths. Whilst I'm not versed in the actual manner in which this happens, it was solved by removing %width values for any height and width values for graphic elements within the button skins. (Eg changing width="100%" to left="0" right="0").
I hope this might help someone, somewhere, sometime.

Scrolling through list element causes text elements to scroll as well

I'm using a list element with variableRowHeight and word-wrap set to true like in the example below:
http://blog.flexexamples.com/2007/10/27/creating-multi-line-list-rows-with-variable-row-heights/
When I scroll through the list with a mouse scrollwheel the text in the listItems also scroll. I know that this is to do with the height of the textField... but I am unsure how to best resolve this issue. Any thoughts are appreciated!
The issue that I am experiencing also occurs in the example above.
OK, solved it after a bit of work. Thought I would put it here for others:
This can be simply solved by extending the List element, and the ListItemRenderer and modifying a couple of lines:
First up, extend the List element:
package au.com.keeghan.controls {
import mx.controls.List;
import mx.core.ClassFactory;
public class ExtendedList extends List{
public function ExtendedList(){
super();
itemRenderer = new ClassFactory(ExtendedListItemRenderer);
}
}
}
Now we want to extend the newly created item renderer (ExtendedListItemRenderer). Because there isn't actually that much code required, we can just put it in the same .as file. We do this by declaring it as a internal class, and locating it outside of the package above... just below the closing bracket:
import mx.controls.listClasses.ListItemRenderer;
internal class AsOneListItemRenderer extends ListItemRenderer{
override protected function measure():void{
super.measure();
var w:Number = 0;
if (icon)
w = icon.measuredWidth;
// Guarantee that label width isn't zero
// because it messes up ability to measure.
if (label.width < 4 || label.height < 4)
{
label.width = 4;
label.height = 16;
}
if (isNaN(explicitWidth))
{
w += label.getExplicitOrMeasuredWidth();
measuredWidth = w;
measuredHeight = label.getExplicitOrMeasuredHeight();
}
else
{
measuredWidth = explicitWidth;
label.setActualSize(Math.max(explicitWidth - w, 4), label.measuredHeight + 3);
label.validateNow();
label.height = label.textHeight + 5;
measuredHeight = label.getExplicitOrMeasuredHeight() + 3;
if (icon && icon.measuredHeight > measuredHeight){
measuredHeight = icon.measuredHeight;
}
}
}
}
Now, the majority of the above code is actually just copied from the ListItemRenderer, the magic occurs down the bottom... specifically these lines:
label.setActualSize(Math.max(explicitWidth - w, 4), label.measuredHeight + 3);
label.validateNow();
label.height = label.textHeight + 5;
measuredHeight = label.getExplicitOrMeasuredHeight() + 3;
All I do here is add some height to both the label, and the overall measuredHeight which in the end is the thing that is causing this issue.
The only downside to this solution is that you will get a larger amount of padding below the listItem element, however you can still make this look good by playing around with the verticalAlign and padding css properties.

Resources