📄 randomwalk.as
字号:
// 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.
// First, we're not assuming the hilight exists. We could take the same tact we do with other styles and use a default if nothing is specified,
// but we want to allow a developer to just not specify a highlight at all, so we'll guard against it here.
if(_highlight != null)
{
if(_highlightedNode == null)
{
// nothing's highlighted, so we'll just hide the highlight.
_highlight.visible = false;
}
else
{
// we've got a highlighted renderer, so position the highlight to match it.
_highlight.visible = true;
var highlightedInstance:UIComponent = dataToInstance(_highlightedNode);
_highlight.move(highlightedInstance.x, highlightedInstance.y);
_highlight.setActualSize(highlightedInstance.width,highlightedInstance.height);
}
}
}
// 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 = verticalGapWithDefault;
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;
}
override protected function keyDownHandler(event:KeyboardEvent):void
{
var n:int = _renderers.length;
var node:XML;
if (_highlightedNode != null)
node = _highlightedNode;
else if (_renderers.length > 0)
{
var stack:Array = _renderers[_renderers.length-1];
node = instanceToData(stack[stack.length-1]);
}
else
{
return;
}
var m:int = node.parent().children().length();
var index:int = node.childIndex();
switch (event.keyCode)
{
case Keyboard.DOWN:
index = (index + 1) % m;
_highlightedNode = node.parent().children()[index];
invalidateDisplayList();
break;
case Keyboard.UP:
index -= 1;
if (index < 0)
index += m;
_highlightedNode = node.parent().children()[index];
invalidateDisplayList();
break;
case Keyboard.RIGHT:
case Keyboard.ENTER:
expandItem(node);
_highlightedNode = (node.children().length() > 0)? node.children()[0]:node;
break;
case Keyboard.LEFT:
var p2:XML;
if(_selectedPathIndices.length == _renderers.length)
{
// if our selected path is the same length as our renderer
// count, it means that our last selected item was a leaf node.
// which means we only want to back up a single level.
p2 = node.parent();
if(p2 != null)
expandItem(p2);
_highlightedNode = node;
}
else
{
// our node is pointing to an item in a set of children with no
// selected item. We want to back up and select its parent's parent,
// which means its parent will be in the set of children with no selection.
p2 = node.parent().parent();
if(p2 != null)
expandItem(p2);
_highlightedNode = node.parent();
}
break;
}
}
/* ----------------------------------------------------------------------------------------
/ History Management
*/
public function loadState(state:Object):void
{
if (state != null)
{
// extract the path from the state object and assign values
// to the _selectedPathIndices array
var path:Array = state.path.split(",");
_selectedPathIndices = new Array(path.length);
for (var i:int = 0; i < _selectedPathIndices.length; i++)
_selectedPathIndices[i] = int(path[i]);
}
else
{
// no state object (initial state)
_selectedPathIndices = [];
}
// since we're potentially throwing away a part of the selected path, we want to throw away any renderers
// we were using for that portion.
if(_selectedPathIndices.length < _renderers.length)
{
var deadInstances:Array = _renderers.splice(_selectedPathIndices.length,(_renderers.length - _selectedPathIndices.length));
for(var j:int=0;j<deadInstances.length;j++)
removeInstances(deadInstances[j]);
}
// since our selected path changed, we'll need to update our renderers. That's expensive, so we'll defer it until our next
// commitProperties call.
invalidateProperties();
}
public function saveState():Object
{
// return a state object containing the path as a comma-separated
// string of index values
return {path: _selectedPathIndices.join(",")};
}
private function addedHandler(event:Event):void
{
if (event.target != this)
return;
if (historyManagementEnabled)
// if this object has been added to the display list,
// register it with the history manager
HistoryManager.register(this);
}
private function removedHandler(event:Event):void
{
if (event.target != this)
return;
if (historyManagementEnabled)
// if this object has been removed from the display list,
// unregister it from the history manager
HistoryManager.unregister(this);
}
}
}
// 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 + -