Recipe 6.3. Moving
Objects Forward and Backward
Problem
You want to change the order in which objects
are drawn on-screen, moving them either in front of or behind other
display objects.
Solution
Use the setChildIndex( ) method of the
DisplayObectContainer
class to change the position of a particular item. Use the
getChildIndex( ) and
getChildAt( ) methods
to query siblings of the item so the item can be positioned
properly relative to them.
Discussion
Recipes 6.1
and 6.2
introduced how the display list model deals with the visual
stacking order (depth). Essentially, every
DisplayObjectContainer instance has a list of children, and
the order of the children in this list determines the order in
which child display objects are drawn inside of the container. The
children are each given a position index, ranging from 0 to
numChildren 1, much like an array. The child at
position 0 is drawn on the bottom, underneath the child at position
1, etc. There are no empty position values in the list; if there
are three children, the children will always have index values of
0, 1, and 2 (and not, say, 0, 1, and 6).
The setChildIndex( ) method is provided
by DisplayObjectContainer to reorder the children inside the
container. It takes two parameters: a reference to the child to be
moved and the child's new position in the container. The index
position specified must be a valid value. Negative values or values
too large will generate a RangeError and the function won't
execute properly.
The following example creates three colored
circles, with the blue one being drawn on top. The
setChildIndex( ) method is used to move the blue circle
underneath the two other circles, changing its position from 2 to 0
in the container. The positions of the other children are adjusted
accordingly; red is moved to 1 and green is moved to 2:
package {
import flash.display.*;
public class SetChildIndexExample extends Sprite {
public function SetChildIndexExample( ) {
// Create three different colored circles and
// change their coordinates so they are staggered
// and aren't all located at (0,0).
var red:Shape = createCircle( 0xFF0000, 10 );
red.x = 10;
red.y = 20;
var green:Shape = createCircle( 0x00FF00, 10 );
green.x = 15;
green.y = 25;
var blue:Shape = createCircle( 0x0000FF, 10 );
blue.x = 20;
blue.y = 20;
// Add the circles, red has index 0, green 1, and blue 2
addChild( red );
addChild( green );
addChild( blue );
// Move the blue circle underneath the others by placing
// it at the very bottom of the list, at index 0
setChildIndex( blue, 0 );
}
// Helper function to create a circle shape with a given color
// and radius
public function createCircle( color:uint, radius:Number ):Shape {
var shape:Shape = new Shape( );
shape.graphics.beginFill( color );
shape.graphics.drawCircle( 0, 0, radius );
shape.graphics.endFill( );
return shape;
}
}
}
One of the requirements for setChildIndex(
) is that you know the index value you want to give to a
specific child. When you're sending a child to the back, you use 0
as the index. When you want to bring a child to the very front, you
specify numChildren 1 as the index. But what if
you want to move a child underneath another child?
For example, suppose you have two circlesone
green and one blueand you don't know their positions ahead of time.
You want to move the blue circle behind the green one, but
setChildIndex( ) requires an integer value for the new
position. There are no setChildAbove or setChildBelow
methods, so the solution is to use the getChildIndex( )
method to retrieve the index of a child, and then use that index to
change the position of the other child. The getChildIndex( )
method takes a display object as a parameter and returns the index
of the display object in the container. If the display object
passed in is not a child of the container, an ArgumentError
is thrown.
The following example creates two circlesone
green and one blueand uses getChildIndex( ) on the green
circle so the blue circle can be moved beneath it. By setting the
blue circle to the index that the green circle has, the blue circle
takes over the position and the green circle moves to the next
higher position because blue had a higher position initially:
package {
import flash.display.*;
public class GetChildIndexExample extends Sprite {
public function GetChildIndexExample( ) {
// Create two different sized circles
var green:Shape = createCircle( 0x00FF00, 10 );
green.x = 25;
green.y = 25;
var blue:Shape = createCircle( 0x0000FF, 20 );
blue.x = 25;
blue.y = 25;
// Add the circles to this container
addChild( green );
addChild( blue );
// Move the blue circle underneath the green circle. First
// the index of the green circle is retrieved, and then the
// blue circle is set to that index.
setChildIndex( blue, getChildIndex( green ) );
}
// Helper function to create a circle shape with a given color
// and radius
public function createCircle( color:uint, radius:Number ):Shape {
var shape:Shape = new Shape( );
shape.graphics.beginFill( color );
shape.graphics.drawCircle( 0, 0, radius );
shape.graphics.endFill( );
return shape;
}
}
}
When a child is moved to an index lower than the
one it currently has, all children from the target index up to the
one just before the child index will have their indexes increased
by 1 and the child is assigned to the target index. When a child is
moved to a higher index, all children from the one just above the
child index up to and including the target index are moved down by
1, and the child is assigned the target index value.
In general, if object a is above
object b, the following code to moves
a directly below b:
setChildIndex( a, getChildIndex( b ) );
Conversely, if object a is below
object b, the preceding code moves
a directly above b.
So far, we've always been moving around children
that we've had a reference to. For example, the
blue variable referenced the display object for the
blue circle, and we were able to use this variable to change the
index of the blue circle. What happens when you don't have a
reference to the object you want to move, and the
blue variable doesn't exist? The setChildIndex(
) method requires a reference to the object as its first
parameter, so you'll need to get the reference somehow if it isn't
available with a regular variable. The solution is to use the
getChildAt( ) method.
The getChildAt( ) method takes a single
argument, an index in the container's children list, and returns a
reference to the display object located at that index. If the
specified index isn't a valid index in the list, a RangeError is thrown.
The following example creates several circles of
various colors and sizes and places them at various locations on
the screen. Every time the mouse is pressed, the child at the very
bottom is placed on top of all of the others:
package {
import flash.display.*;
import flash.events.*;
public class GetChildAtExample extends Sprite {
public function GetChildAtExample( ) {
// Define a list of colors to use
var color:Array = [ 0xFF0000, 0x990000, 0x660000, 0x00FF00,
0x009900, 0x006600, 0x0000FF, 0x000099,
0x000066, 0xCCCCCC ];
// Create 10 circles and line them up diagonally
for ( var i:int = 0; i < 10; i++ ) {
var circle:Shape = createCircle( color[i], 10 );
circle.x = i;
circle.y = i + 10; // the + 10 adds padding from the top
addChild( circle );
}
stage.addEventListener( MouseEvent.CLICK, updateDisplay );
}
// Move the circle at the bottom to the very top
public function updateDisplay( event:MouseEvent ):void {
// getChildAt(0) returns the display object on the
// very bottom, which then gets moved to the top
// by specifying index numChildren - 1 in setChildIndex
setChildIndex( getChildAt(0), numChildren - 1 );
}
// Helper function to create a circle shape with a given color
// and radius
public function createCircle( color:uint, radius:Number ):Shape {
var shape:Shape = new Shape( );
shape.graphics.beginFill( color );
shape.graphics.drawCircle( 0, 0, radius );
shape.graphics.endFill( );
return shape;
}
}
}
See Also
Recipes 6.1
and 6.2
|