📄 simplerandomwalk.as
字号:
// (retains) all the drawing you do until you explicitly tell it to clear it. That means that even if you draw the same rectangle, flash remembers
// the previous rectangle, and now draws both of them on screen. Do this enough, and you'll see your application slow down trying to render all of those shapes.
// so always remember to clear before redrawing.
_lineSprite.graphics.clear();
// first thing we do is draw our border.
_lineSprite.graphics.lineStyle(2,0);
_lineSprite.graphics.beginFill(0xFFFFFF);
_lineSprite.graphics.drawRect(0,0,unscaledWidth,unscaledHeight);
_lineSprite.graphics.endFill();
// we're going to lay out our stacks of renderers, left to right. We'll initialize this variable to point to where our first stack will go.
var left:Number = 4*GAP_FROM_LEFT_RENDERER_EDGE;
var details:StackDetails;
// this is going to be a two pass layout process. First, we'll lay out each stack left to right, top to bottom. Then we'll iterate through our stacks in
// reverse order, and try and align the selected item in each stack, from right to left. In the first pass, we're going to calculate some information that
// will prove useful in the second pass. So we'll store off that information in a temporary array for later use.
var detailsList:Array = [];
// the value of our horizontalGap style. Since calling getStyle (and guarding against bad values) is a mildly expensive operation, it's a good idea to store off
// style values into a local variable if you're going to accessing them repeatedly in a function.
var horizontalGap:Number = HORIZONTAL_GAP;
// OK, time for our first pass. We're going to iterate over our stacks of renderers, laying them out left to right, top to bottom, and calculating critical
// size details as we go.
for(var i:int = 0;i<_renderers.length;i++)
{
// lay out the Nth stack
details = renderStack(_renderers[i], Number(_selectedPathIndices[i]), left);
// store off the details we calculated to use in our second pass
detailsList.push(details);
// advance our marker for where the next stack should be positioned horizontally.
left = details.right + horizontalGap;
}
// Ok, we're going to perform our second layout pass here. The idea is this: We want to adjust the vertical positioning of our selected items so we get a nice straight line.
// To get the effect we're going for, we want to lay out the last selected item first, and then work our way backwards. The one catch is that our last stack or renderers
// don't have anything selected yet. So we'll start with the second to last one.
var selectionBaselineTarget:Number;
if (_selectedPathIndices.length == 0)
{
// we've only got one open stack, with nothing selected, so just align that one and call it a day.
alignStack(_renderers[0],detailsList[0],selectionBaselineTarget);
}
else if (_selectedPathIndices.length > 0)
{
// ok, we've got at least one with a selected item. So start
// with the last selected item and align it.
i = _selectedPathIndices.length-1;
selectionBaselineTarget = alignStack(_renderers[i],detailsList[i],selectionBaselineTarget);
// now let's adjust the last stack, the unselected one. He'll just try and be centered on the previous selection.
if(i+1 < _renderers.length)
alignStack(_renderers[i+1],detailsList[i+1],selectionBaselineTarget);
for(i--;i>=0;i--)
{
// now, working backwards, try and align the selection to the baseline of the previous
// selection.
selectionBaselineTarget = alignStack(_renderers[i],detailsList[i], selectionBaselineTarget);
}
}
// Now we want to draw the line that underscores the individual items. Here we're going to use the
// graphics API. If we were making a truly configurable component, we might package up the line drawing into
// a sub-component and allow developers to swap in a different implementation using skinning/CSS. There's a grey
// area between skinning and itemRenderers here, which this definitely falls into.
if(_renderers.length > 0)
{
// start at the baseline of the selected item in the first stack
// HEY! Look! hardcoded graphical properties. That's a red flag that these values (the line color, weight, and alpha)
// are probably something we should move into styles for an easy extra bit of configuration.
_lineSprite.graphics.lineStyle(1,0xAAAAAA);
details = detailsList[0];
_lineSprite.graphics.moveTo(details.left - GAP_FROM_LEFT_RENDERER_EDGE,details.selectionBaseline);
// now, for each stack,
for(i=0;i<_renderers.length;i++)
{
// grab those layout details we stored off earlier
details = detailsList[i];
if(i < _selectedPathIndices.length)
{
// first draw from the end of the previous selection vertically to the beginning of the
// baseline of this selection
_lineSprite.graphics.lineTo(details.left - GAP_FROM_LEFT_RENDERER_EDGE,details.selectionBaseline);
// now draw horizontally across the selection's baseline.
_lineSprite.graphics.lineTo(details.right + horizontalGap - GAP_FROM_LEFT_RENDERER_EDGE,details.selectionBaseline);
}
else if (_renderers[i].length > 0)
{
// there's no selection for this stack, so we'll just draw a vertical line
// down the entire stack.
_lineSprite.graphics.moveTo(details.left-GAP_FROM_LEFT_RENDERER_EDGE,details.top);
_lineSprite.graphics.lineTo(details.left-GAP_FROM_LEFT_RENDERER_EDGE,details.bottom);
}
}
}
// Lastly, if the mouse is currently over an item, we want to provide a nice rollover effect.
// Given that this component is using item renderers, we could just leave that as behavior the item
// renderer should implement. But in this case, we're going to provide the functionality to save
// our developers the trouble.
if(_highlightedInstance != null)
{
// we've got a highlighted renderer, so position the highlight to match it.
_lineSprite.graphics.moveTo(_highlightedInstance.x,_highlightedInstance.y);
_lineSprite.graphics.lineStyle(1,0);
_lineSprite.graphics.beginFill(0xCCCCCC);
_lineSprite.graphics.drawRect(_highlightedInstance.x,_highlightedInstance.y,_highlightedInstance.width,_highlightedInstance.height);
_lineSprite.graphics.endFill();
}
}
// This internal utility function is the one that does the layout for our first pass.
// it positions a single stack vertically and horizontally, and saves off details
// about the dimension of the stack.
private function renderStack(instances:Array, selectedIndex:Number, left:Number):StackDetails
{
var maxWidth:Number = 0;
var top:Number = 0;
var selectionBaseline:Number;
var i:int;
var verticalGap:Number = VERTICAL_GAP;
var selectedInstance:UIComponent = null;
// first, we want all of the renderers in our stack to be the same size,
// so we're going to iterate through them and get the maximum length of
// the renderers.
for(i = 0;i<instances.length;i++)
{
// note that we're calling the convenience function, get ExplicitOrMeasuredWidth function,
// to decide how big these components should be. This function returns either the explicitly assigned
// pixel size of the component, if someone has assigned one, or its measured size otherwise.
// Note that if you want to support percentage based sizes in your child components, you'll need to account for
// that separately.
// Given that we need this information when we do measurement, if we were good developers we'd have cached it then.
maxWidth = Math.max(maxWidth,instances[i].getExplicitOrMeasuredWidth());
}
// OK, now we actually stack our components, top to bottom.
for(i = 0;i<instances.length;i++)
{
// for each component
var inst:UIComponent = instances[i];
// getits explicit/measured sizes
var eomHeight:Number = inst.getExplicitOrMeasuredHeight();
var eomWidth:Number = maxWidth;
inst.setActualSize(eomWidth,eomHeight);
inst.move(left,top);
// if this renderer represents a selected item, we're going to remember that, because later on we'll want
// to store its baseline in our cached details.
if(i == selectedIndex)
{
selectedInstance = inst;
selectionBaseline = top + eomHeight;
}
// advance our marker for where the top of the next component should go.
top += eomHeight + verticalGap;
}
// store off the information we've calculated that we'll be needing later.
var details:StackDetails = new StackDetails();
details.top = 0;
details.bottom = top - verticalGap;
details.left = left;
details.right = left + maxWidth;
details.selectedIndex = selectedIndex;
details.selectionBaseline = selectionBaseline;
return details;
}
// This internal utility function does the processing for the second pass of our layout.
// this function gets passed a stack of renderers that have already been positioned horizontally, and
// vertically relative to each other...a set of details describing the stack, and a target y pixel value
// for where we want to place the baseline of the selected renderer. This function will
// adjust the renderers vertically to try and match this value.
private function alignStack(instances:Array, details:StackDetails,selectionBaselineTarget:Number):Number
{
//
var stackHeight:Number = details.bottom - details.top;
var selectionBaseline:Number;
var i:int;
var inst:UIComponent;
// first, grab the current position of our the baseline of our selected component.
// this is the point in the stack that we want to move to line up with our target.
// if we don't have a selection, then we're going to want to try and align the center
// of the stack with the target.
selectionBaseline = details.selectionBaseline;
if(isNaN(selectionBaseline))
selectionBaseline = stackHeight/2;
// OK, the caller should have told us where they want us to align our selection,
// relative to the component. If they didn't tell us, then we'll just assume we want
// to try and align it with the center of the component.
if(isNaN(selectionBaselineTarget))
selectionBaselineTarget = unscaledHeight/2;
// now that we have our anchor point in the stack, and our target location for it,
// calculate how much we need to shift the renderers to line those two numbers up.
var verticalOffset:Number = selectionBaselineTarget - selectionBaseline ;
// we don't want to burst out of the bounds of our component. So if
// lining those two numbers up would push the stack off the top, reduce the adjustment
// to get as close as we can
if(verticalOffset + details.top < GAP_FROM_TOP_EDGE)
verticalOffset = GAP_FROM_TOP_EDGE-details.top;
// same check to make sure our stack doesn't burst off the bottom of the component.
if(stackHeight + verticalOffset > unscaledHeight - GAP_FROM_TOP_EDGE)
verticalOffset = unscaledHeight - GAP_FROM_TOP_EDGE - stackHeight;
// alright, we've got a safe adjustment calculated, so
// let's shift our renderers.
for(i = 0;i<instances.length;i++)
{
inst = instances[i];
inst.move(inst.x,inst.y + verticalOffset);
}
// update our details.
details.selectionBaseline = selectionBaseline + verticalOffset;
details.top += verticalOffset;
details.bottom += verticalOffset;
// our layout pass tries to line up our selections. Since we can't guarantee
// that our selection will end up in the middle of the component (if, for example,
// our stack would have pushed off the edge), we need to tell the caller where
// our anchor point ended up.
return selectionBaseline + verticalOffset;
}
}
}
// This is a utility class that the layout routine for our component uses. Flex allows you to declare classes inside the file, outside of the package declaration.
// These classes are visible only to code inside this file. This is a useful way to create small utility structs and classes meant only for implementation purposes.
class StackDetails
{
// the position top of the stack, in pixels
public var top:Number;
// the position of the bottom of the stack, in pixels;
public var bottom:Number;
// the position of the left of the stack, in pixels;
public var left:Number;
// the position of the right of the stack, in pixels;
public var right:Number;
// the index of the selected item in the stack, if it exists. NaN otherwise.
public var selectedIndex:Number;
// the vertical position of the baseline of the selected item in the stack, if it exists, in pixels.
public var selectionBaseline:Number;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -