I am building large application in Starling and one of my main problems is who should I layout first: the parent or the children?
What is the default behavior in starling and also in flash:
By default Sprite will get his size based on his children after they have been added to stage.
What if I want to layout the children based on the parent? For example: What if I want to set one of the children to be at position of 20 pixels from the bottom, like bottom menu?
In this case I should:
Add the children
Determine their sizes. If you are building your application cross platform, you need to support many screens, and many times you come to have complicate logic for calculating the scale percentage of your components, which is their size.
Determine your size and layout yourself.
now the bottom menu could be layout at 20 pixels from the bottom. Also it doesn't matter if you place this logic inside the bottom menu or it's parent.
But this not always the case, sometimes you want to layout the parent based on his children. A common example if one of the children is the parent background. In this case you should:
Add the background.
Determinate background size and layout it.
Now you can layout the parent.
But what if I got both of the cases? If one of parent children is background and the other is bottom menu? What if the bottom menu got his own background and other children that need to be layouted base on the parent?
What solution can be used so I will not get lost inside all of this, and can Gazman SDK help here?
What you need is to create layout dependencies between all the components. Each Sprite should have an event that tells us when its layouting is complete.
Now if you have some layouting logic inside the parent that cannot start until its background child is complete layouting, you should create a dependency between the background and the parent. The parent should listen to LayoutComplete event from the background and then he can layout himself, and when he complete layouting he can dispatch LayoutComplete event, and now its child bottom menu can layout himself.
You can implement it yourself or use Gazman-SDK that do exactly that. If you choose Gazman-SDK your code will look like this:
public class ParentClass extends Group
{
private var background:Background = new Background();
private var bottomMenu:BottomMenu = new BottomMenu();
override protected function initialize():void
{
// check if not already added to stage
if(!background.parent){
addChild(background);
addChild(bottomMenu);
}
// Create dependency for background. If background have not
// been initialized yet the subscription will succeed
// And this.initialize() will be called again once
// background initialize is complete
if (subscribeForInitilize(background)){
return;
}
// do layouting logic here
}
}
public class BottomMenu extends Group
{
override protected function initialize():void
{
// Create dependency for parent. If parent have not
// been initialized yet the subscription will succeed
// And this.initialize() will be called again once
// parent initialize is complete
if (subscribeForInitilize(parent as Group)){
return;
}
// do layouting logic here
}
}
Related
I'm working on a UI project that requires that the automatic resizing buttons (based on label length) have a focus indicator graphic for keyboard or controller focus. This graphic must obviously scale with the parent as you would imagine.
This causes a problem; the parent clip is 9sliced, and that slicing doesn't fall through to the child Sprites/MovieClips of this clip. The focus indicator needs to be an accessible property because it has to be capable of being turned on or off.
Currently the only solution I can image is an extremely programmatical reimplementation of scale9Grid where I split the focus indicator into 9 and alter the 9 parts' properties any time the parent width/height/scaleX/scaleY is changed. This would also mean turning all 9 parts on and off when that button is focused
Is there any better way than that?
I recommend you create some wrapper class AppButton (or may be you already have one since you have some resizing by label functionality) with method setSkin(skin:MovieClip) (where skin is your MovieClip from the library) and overridden setters for width and height, so you can implement here skin resizing logic in method arrange() that called each time width or height are changed.
Skin can be complex - with other movie clips in children (focus border in your case), so don't use scale9Grid for the hole skin, but set sizes directly to the children with set scale9grid them as well, so your arrange method can be like that:
private function arrange():void
{
var child:DisplayObject;
for(var i:int = 0; i < numChildren; i++)
{
child = getChildAt(i);
child.width = width;
child.height = height;
}
}
It's also worth to make one skin format for button skins in project, so you can use one wrapper for all buttons.
Later you can add more features to this AppButton - switching view states on mouse events, setting text label, animating skins and so.
This approach work for me for many years, we have base ToggleButton and LabelButton extends ToggleButton classes, and extends them in every project with custom skin parsing and arranging.
I am developing a 2D games with cocos2dx, in which I am still very new... inside the game, there's a number of UI Elements that I'd like to group together as one (I intend to group them into CCLayers). For example a few text labels and sprites form a CStatBar which is a CCLayer. This CStatBar will then be included in the various other CCLayer
How do I do that? I created the CStatBar class and then, inside the containing class's init() function, I CStatBar::create() and call this->addChild(pStatBar) however, the statbar did not appear... is there any obvious thing that I missed? All the positions are correct. Thanks!
EDIT:
Notes:
1. ccTouchesBegan of the sublayer is called, however it is not rendered/seen
2. How do I resize the sublayer so that it only cover partial area of the parent layer? Supposedly the CStatBar should only cover 10% of the top area of the screen, not the whole screen...
inside the CParent::init() function, you can initialize the CSublayer like so:
// Create and initialize CSublayer
CSublayer* pSublayer= CSublayer::create();
float fWidth = pSublayer->getContentSize().width;
float fHeight = pSublayer->getContentSize().height;
pSublayer->setPosition(ccp(fWidth/2.0f, fHeight/2.0f));
this->addChild( pSublayer );
and your CSublayer can be defined like other CCLayer.
If you want to restrict the CSublayer to be smaller than the CParent layer, you can do so inside its init function like so:
CSublayer::init() {
// initialize the size with the size of the background sprite
CCSprite *pSpriteBackground = CCSprite::createWithSpriteFrame(
CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName("background.png")
);
this->setContentSize(CCSize(pSpriteBackground->getContentSize().width, pSpriteBackground->getContentSize().height));
pSpriteBackground->setPosition(ccp(fScreenHalfWidth, fScreenHeight-(pSpriteBackground->getContentSize().height/2.0f)));
this->addChild(pSpriteBackground);
}
I am writing a class that extends mx.core.UIComponent. In the constructor of this class, I would like to add a Button (spark.components.Button). However, creating a new Button object (such as var b:Button = new Button();) and then doing this.addChild(b) doesn't work -- no button is appearing. Is there any reason why a Button object can't be added as a child?
Edit: Just to add, when I print out the children, it says that the Button object is a child, but the Button doesn't show up.
You need to add the button in the createChildren method not in the constructor.
protected var button:Button;
override protected function createChildren():void {
super.createChildren();
button = new Button();
addChild(button);
}
Depending on what you are trying to do you may also need to implement the measure and updateDisplayList methods.
Using UIComponent directly, you aren't getting the layout of the child handled for you; you would need to set the position and size of your child components manually (see docs).
Consider using a Group (or Canvas, if you're still on MX components) to handle child layout. You can still add rendering in updateDisplayList, just make sure you call super.updateDisplayList() as well.
You need to read up on the differences between ActionScript components and Flex components. When using a Flex component, you must use container.addElement( element ), not container.addChild( child ). As far as I am aware, the only classes with this functionality descend from Group (at least in the Spark framework, the one I am most familiar with). So extending UIComponent will not allow you to add a Button, although a UIComponent can be added to a Flex container (which I do not believe a normal Sprite can do)
I'm building a game of which the interface is one of the first items to load on screen. Sound button, pause and all the bits -
During the game - all manor of things are dynamically added and removed to the stage. Therefore my interface goes behind my game scene.
How do I ensure the movieclip always stays on top?
Can I override the addChild of my Document Class and every time a new child is added, I restack the interface to the top?
You can use setChildIndex to rearrange objects that are already contained within a MovieClip or Sprite. For example, if you have an object called container, which contains a redBall, blueBall and many other ball objects, you can use the following to make sure redBall is behind everything else:
container.setChildIndex(redBall, 0);
Equally you can make sure that blueBall will be displayed infront of everything else using:
container.setChildIndex(blueBall, container.numChildren-1);
addChildAt will sort you out for adding children straight into their correct position, and setChildIndex will allow you to re-position objects already placed. Hope that helps.
debu
Look into addChildAt, it allows you to specify the index that the new object should be added too.
http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/display/DisplayObjectContainer.html#addChildAt%28%29
A good strategy is to keep all interface elements in one parent Sprite/MovieClip, and all game assets in another. (With the interface one on top)
With your guys suggestion I made this, apparently, if you addChild an object already on the screen, it's simply reindex'd to the top. Does this look ok?
private var topLevelChildrenArray:Array = [];
public function addTopLevelChild(child:DisplayObject):DisplayObject
{
topLevelChildrenArray.push(child)
return this.addChildAt( child, this.numChildren - 1 );
}
override public function addChild(child:DisplayObject):DisplayObject
{
this.addChildAt(child, this.numChildren);
for each(var topChild:DisplayObject in topLevelChildrenArray)
{
super.addChild(topChild);
}
return child
}
Anyone know how to animate the size/position of child elements of a layout in Flex 4 ?
Example:
I have a list component with a custom layout. I want when I change the positions of the child elements I want them to animate their move to the new positions.
There is currently no built in way, nor plans, to make animated layouts in Flex 4 :/.
What I've done to make this happen is to animate the setting of "setLayoutBoundsPosition" and "setLayoutBoundsSize" in the layout. So instead of creating a "Move" and "Resize" effect for each item in the layout, which would actually set the width and height explicitly, set the matrix. Then make sure the layout isn't invalidated again (which will happen if you set the width/height directly), or you might start getting an infinite loop. You might have to do some tricks to get this to work right (I haven't got it to work quite right with the Spark Effects, but it's really easy to do with Tweener/Tweenmax, since they have plugins and such to use "setActualSize" or "setLayoutBoundsSize", etc.).
I use TweenMax to animate layouts, and they have a few plugins to make this easy. TweenMax visibly looks like 3x faster (20 fps vs 7fps) than Spark Effects, too, so I'd go with that. It looks something like this, in the updateDisplayList method of your layout.
TweenMax.to(child, duration, {setLayoutBoundsPosition:{x:childX * i, y:childY * i}});
Just like you would normally...
public override function updateDisplayList(width:Number, height:Number) : void {
for (var i:uint = 0; i < target.numElements; i++)
{
var resizeElement:Resize = new Resize(target.getElementAt(i) as IVisualElement);
resizeElement.widthTo = 500;
resizeElement.play();
}
}